未找到已加载动态功能的主要 activity class
Main activity class of loaded Dynamic feature is not found
我正在尝试为我的应用程序添加动态功能支持,因此我创建了一个测试应用程序。
测试应用有一个加载动态功能并尝试执行它的主要应用部分。
功能模块有 MainActivityCalled 为主activity。
我得到的是功能加载过程有效,因为我收到了成功的日志消息,并且我得到了已安装模块的列表。
请注意,该应用程序 运行 在虚拟设备上,并没有真正的下载,我认为一切都是通过 AndroidStudio 的部署过程自动安装的。
事实上,我在尝试调用模块的主要 activity 时遇到这种错误:
W/System.err: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.dynamicfeature1/MainActivityCalled}; have you declared this activity in your AndroidManifest.xml?
W/System.err: at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1805)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
at android.app.Activity.startActivityForResult(Activity.java:4225)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767)
at android.app.Activity.startActivityForResult(Activity.java:4183)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754)
at android.app.Activity.startActivity(Activity.java:4522)
at android.app.Activity.startActivity(Activity.java:4490)
at com.example.mymodules.MainActivity.onClick(MainActivity.java:227)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
一切都按照 https://developer.android.com/guide/app-bundle/playcore#java 上的文档完成
除了对模块 activity 的显式调用外,我猜想必须有意执行(上面链接页面中没有示例)。
您在下面找到的代码的重要部分是:
new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer result) {
Log.d("request feature load","success "+result);
mySessionId=result;
Set<String> installedModules = splitInstallManager.getInstalledModules();
String[] modules = new String[installedModules.size()];
installedModules.toArray(modules);
for (int i=0;i<modules.length;i++)
{
Log.d("module",modules[i]);
}
}
})
没关系。
然后
Button button2 = findViewById(R.id.fab2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("feature1","called");
Intent intent=new Intent();
intent.setClassName("com.example.dynamicfeature1","MainActivityCalled");
try{ startActivity(intent);}
catch (Exception e){
e.printStackTrace();
}
}
});
所有主要活动都有此重写方法
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Emulates installation of on demand modules using SplitCompat.
SplitCompat.installActivity(this);
Log.d("attachBaseContext",base.getPackageName().toString());
}
安装时动态特性模块中没有调用。
我的代码有什么问题?
这是应用程序的主activity
package com.example.mymodules;
...imports...
public class MainActivity extends AppCompatActivity {
private static int MY_REQUEST_CODE=1;
Activity activity;
int mySessionId;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Emulates installation of future on demand modules using SplitCompat.
SplitCompat.install(this);
Log.d("attachBaseContext",base.getPackageName().toString());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_REQUEST_CODE) {
// Handle the user's decision. For example, if the user selects "Cancel",
// you may want to disable certain functionality that depends on the module.
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity = this;
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button button1 = findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Creates an instance of SplitInstallManager.
final SplitInstallManager splitInstallManager =
SplitInstallManagerFactory.create(activity);
// Creates a request to install a module.
SplitInstallRequest request =
SplitInstallRequest
.newBuilder()
// You can download multiple on demand modules per
// request by invoking the following method for each
// module you want to install.
.addModule("dynamicfeature1")
.build();
// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener =new SplitInstallStateUpdatedListener() {
@Override
public void onStateUpdate(SplitInstallSessionState state) {
if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
// Displays a dialog for the user to either “Download”
// or “Cancel” the request.
try {
splitInstallManager.startConfirmationDialogForResult(
state,
/* activity = */ activity,
// You use this request code to later retrieve the user's decision.
/* requestCode = */ MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
if (state.sessionId() == mySessionId) {
switch (state.status()) {
case SplitInstallSessionStatus.INSTALLED:
Context tempNewContext=null;
try {
tempNewContext = activity.createPackageContext(activity.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
}
final Context newContext =tempNewContext;
// If you use AssetManager to access your app’s raw asset files, you’ll need
// to generate a new AssetManager instance from the updated context.
AssetManager am = newContext.getAssets();
if (BuildCompat.isAtLeastO()) {
// Updates the app’s context with the code and resources of the
// installed module.
SplitInstallHelper.updateAppInfo(newContext);
new Handler().post(new Runnable() {
@Override public void run() {
// Loads contents from the module using AssetManager
AssetManager am = newContext.getAssets();
}
});
} else
{SplitInstallHelper.updateAppInfo(newContext);}
}
}
}
} ;
splitInstallManager.registerListener(listener);
splitInstallManager
// Submits the request to install the module through the
// asynchronous startInstall() task. Your app needs to be
// in the foreground to submit the request.
.startInstall(request)
// You should also be able to gracefully handle
// request state changes and errors. To learn more, go to
// the section about how to Monitor the request state.
.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer result) {
Log.d("request feature load","success "+result);
mySessionId=result;
Set<String> installedModules = splitInstallManager.getInstalledModules();
String[] modules = new String[installedModules.size()];
installedModules.toArray(modules);
for (int i=0;i<modules.length;i++)
{
Log.d("module",modules[i]);
}
}
})
.addOnFailureListener(new OnFailureListener() {
void checkForActiveDownloads() {
splitInstallManager
// Returns a SplitInstallSessionState object for each active session as a List.
.getSessionStates()
.addOnCompleteListener(
new OnCompleteListener<List<SplitInstallSessionState>>() {
@Override
public void onComplete(Task<List<SplitInstallSessionState>> task) {
if (task.isSuccessful()) {
// Check for active sessions.
for (SplitInstallSessionState state : task.getResult()) {
if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
// Cancel the request, or request a deferred installation.
}
}
}
}
});
}
@Override
public void onFailure(Exception e) {
Log.d("request feature load","failure "+e.getMessage());
switch (((SplitInstallException) e).getErrorCode()) {
case SplitInstallErrorCode.NETWORK_ERROR:
// Display a message that requests the user to establish a
// network connection.
break;
case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
checkForActiveDownloads();
}
}
});
}
});
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("feature1","called");
Intent intent=new Intent();
intent.setClassName("com.example.dynamicfeature1","MainActivityCalled");
try{ startActivity(intent);}
catch (Exception e){
e.printStackTrace();
}
}
});
Button button3 = findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("feature2","called");
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
这是主要的应用程序清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.example.mymodules">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name="com.google.android.play.core.splitcompat.SplitCompatApplication"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
然后是动态特性模块的MainActivity
package com.example.dynamicfeature1;
...imports...
public class MainActivityCalled extends AppCompatActivity {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Emulates installation of on demand modules using SplitCompat.
SplitCompat.installActivity(this);
Log.d("attachBaseContext",base.getPackageName().toString());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "1-Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
}
和模块清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.example.dynamicfeature1">
<dist:module
dist:instant="false"
dist:title="@string/title_dynamicfeature1">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="false" />
</dist:module>
<application>
<activity
android:name=".MainActivityCalled"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBar"></activity>
</application>
</manifest>
调用 activity 的正确指令是
intent. setClassName(BuildConfig.APPLICATION_ID, "com.example.dynamicfeature.MainActivityCalled");
有效,activity 被调用。
注意BuildConfig.APPLICATION_ID是
com.example.mymodules
如果这个字符串也用在动态模块中,你可以进行交叉调用:
-从一个模块到另一个模块
-从一个模块到主应用程序
但您不想在模块中使用 BuildConfig.APPLICATION_ID,因为它有不同的值,因此必须将主要字符串值放入变量中。
我正在尝试为我的应用程序添加动态功能支持,因此我创建了一个测试应用程序。
测试应用有一个加载动态功能并尝试执行它的主要应用部分。
功能模块有 MainActivityCalled 为主activity。
我得到的是功能加载过程有效,因为我收到了成功的日志消息,并且我得到了已安装模块的列表。
请注意,该应用程序 运行 在虚拟设备上,并没有真正的下载,我认为一切都是通过 AndroidStudio 的部署过程自动安装的。
事实上,我在尝试调用模块的主要 activity 时遇到这种错误:
W/System.err: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.dynamicfeature1/MainActivityCalled}; have you declared this activity in your AndroidManifest.xml?
W/System.err: at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1805)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
at android.app.Activity.startActivityForResult(Activity.java:4225)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767)
at android.app.Activity.startActivityForResult(Activity.java:4183)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754)
at android.app.Activity.startActivity(Activity.java:4522)
at android.app.Activity.startActivity(Activity.java:4490)
at com.example.mymodules.MainActivity.onClick(MainActivity.java:227)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
一切都按照 https://developer.android.com/guide/app-bundle/playcore#java 上的文档完成 除了对模块 activity 的显式调用外,我猜想必须有意执行(上面链接页面中没有示例)。
您在下面找到的代码的重要部分是:
new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer result) {
Log.d("request feature load","success "+result);
mySessionId=result;
Set<String> installedModules = splitInstallManager.getInstalledModules();
String[] modules = new String[installedModules.size()];
installedModules.toArray(modules);
for (int i=0;i<modules.length;i++)
{
Log.d("module",modules[i]);
}
}
})
没关系。
然后
Button button2 = findViewById(R.id.fab2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("feature1","called");
Intent intent=new Intent();
intent.setClassName("com.example.dynamicfeature1","MainActivityCalled");
try{ startActivity(intent);}
catch (Exception e){
e.printStackTrace();
}
}
});
所有主要活动都有此重写方法
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Emulates installation of on demand modules using SplitCompat.
SplitCompat.installActivity(this);
Log.d("attachBaseContext",base.getPackageName().toString());
}
安装时动态特性模块中没有调用。
我的代码有什么问题?
这是应用程序的主activity
package com.example.mymodules;
...imports...
public class MainActivity extends AppCompatActivity {
private static int MY_REQUEST_CODE=1;
Activity activity;
int mySessionId;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Emulates installation of future on demand modules using SplitCompat.
SplitCompat.install(this);
Log.d("attachBaseContext",base.getPackageName().toString());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_REQUEST_CODE) {
// Handle the user's decision. For example, if the user selects "Cancel",
// you may want to disable certain functionality that depends on the module.
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity = this;
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button button1 = findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Creates an instance of SplitInstallManager.
final SplitInstallManager splitInstallManager =
SplitInstallManagerFactory.create(activity);
// Creates a request to install a module.
SplitInstallRequest request =
SplitInstallRequest
.newBuilder()
// You can download multiple on demand modules per
// request by invoking the following method for each
// module you want to install.
.addModule("dynamicfeature1")
.build();
// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener =new SplitInstallStateUpdatedListener() {
@Override
public void onStateUpdate(SplitInstallSessionState state) {
if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
// Displays a dialog for the user to either “Download”
// or “Cancel” the request.
try {
splitInstallManager.startConfirmationDialogForResult(
state,
/* activity = */ activity,
// You use this request code to later retrieve the user's decision.
/* requestCode = */ MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
if (state.sessionId() == mySessionId) {
switch (state.status()) {
case SplitInstallSessionStatus.INSTALLED:
Context tempNewContext=null;
try {
tempNewContext = activity.createPackageContext(activity.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
}
final Context newContext =tempNewContext;
// If you use AssetManager to access your app’s raw asset files, you’ll need
// to generate a new AssetManager instance from the updated context.
AssetManager am = newContext.getAssets();
if (BuildCompat.isAtLeastO()) {
// Updates the app’s context with the code and resources of the
// installed module.
SplitInstallHelper.updateAppInfo(newContext);
new Handler().post(new Runnable() {
@Override public void run() {
// Loads contents from the module using AssetManager
AssetManager am = newContext.getAssets();
}
});
} else
{SplitInstallHelper.updateAppInfo(newContext);}
}
}
}
} ;
splitInstallManager.registerListener(listener);
splitInstallManager
// Submits the request to install the module through the
// asynchronous startInstall() task. Your app needs to be
// in the foreground to submit the request.
.startInstall(request)
// You should also be able to gracefully handle
// request state changes and errors. To learn more, go to
// the section about how to Monitor the request state.
.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer result) {
Log.d("request feature load","success "+result);
mySessionId=result;
Set<String> installedModules = splitInstallManager.getInstalledModules();
String[] modules = new String[installedModules.size()];
installedModules.toArray(modules);
for (int i=0;i<modules.length;i++)
{
Log.d("module",modules[i]);
}
}
})
.addOnFailureListener(new OnFailureListener() {
void checkForActiveDownloads() {
splitInstallManager
// Returns a SplitInstallSessionState object for each active session as a List.
.getSessionStates()
.addOnCompleteListener(
new OnCompleteListener<List<SplitInstallSessionState>>() {
@Override
public void onComplete(Task<List<SplitInstallSessionState>> task) {
if (task.isSuccessful()) {
// Check for active sessions.
for (SplitInstallSessionState state : task.getResult()) {
if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
// Cancel the request, or request a deferred installation.
}
}
}
}
});
}
@Override
public void onFailure(Exception e) {
Log.d("request feature load","failure "+e.getMessage());
switch (((SplitInstallException) e).getErrorCode()) {
case SplitInstallErrorCode.NETWORK_ERROR:
// Display a message that requests the user to establish a
// network connection.
break;
case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
checkForActiveDownloads();
}
}
});
}
});
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("feature1","called");
Intent intent=new Intent();
intent.setClassName("com.example.dynamicfeature1","MainActivityCalled");
try{ startActivity(intent);}
catch (Exception e){
e.printStackTrace();
}
}
});
Button button3 = findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("feature2","called");
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
这是主要的应用程序清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.example.mymodules">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name="com.google.android.play.core.splitcompat.SplitCompatApplication"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
然后是动态特性模块的MainActivity
package com.example.dynamicfeature1;
...imports...
public class MainActivityCalled extends AppCompatActivity {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// Emulates installation of on demand modules using SplitCompat.
SplitCompat.installActivity(this);
Log.d("attachBaseContext",base.getPackageName().toString());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "1-Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
}
和模块清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.example.dynamicfeature1">
<dist:module
dist:instant="false"
dist:title="@string/title_dynamicfeature1">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="false" />
</dist:module>
<application>
<activity
android:name=".MainActivityCalled"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBar"></activity>
</application>
</manifest>
调用 activity 的正确指令是
intent. setClassName(BuildConfig.APPLICATION_ID, "com.example.dynamicfeature.MainActivityCalled");
有效,activity 被调用。
注意BuildConfig.APPLICATION_ID是
com.example.mymodules
如果这个字符串也用在动态模块中,你可以进行交叉调用:
-从一个模块到另一个模块
-从一个模块到主应用程序
但您不想在模块中使用 BuildConfig.APPLICATION_ID,因为它有不同的值,因此必须将主要字符串值放入变量中。