Android intent activity in external lib

151 views Asked by At
  1. I want to startActivity the class in the external AndroidLibrary..

    **Error :** Unable to instantiate activity ComponentInfo{com.fsd.ddc/com.fsd.toastlib.ToastActivity}: java.lang.ClassNotFoundException: Didn't find class "com.fsd.toastlib.ToastActivity"
    

    I have already register the activity name in the manifest file

    (Main) com.fsd.ddc.AndroidManifest.xml

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
    
        <activity android:name="com.fsd.toastlib.ToastActivity">
            <intent-filter>
                <action android:name="com.fsd.toastlib.MAIN" />
    
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    
        <activity android:name=".DDCActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".TestActivity"></activity>
    </application>
    

    (Main) com.fsd.ddc.DDCActivity.java

    public class DDCActivity extends AppCompatActivity {
    
    // Buffer size for file copying.  While 8kb is used in this sample, you
    // may want to tweak it based on actual size of the secondary dex file involved.
    private static final int BUF_SIZE = 8 * 1024;
    private static final String SECONDARY_DEX_NAME = "com.fsd.toastlib.jar";
    
    private Button mToastButton = null;
    private ProgressDialog mProgressDialog = null;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ddc);
    
        Button btn_class = (Button) findViewById(R.id.btn_class);
    
        // Before the secondary dex file can be processed by the DexClassLoader,
        // it has to be first copied from asset resource to a storage location.
        final File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),
                SECONDARY_DEX_NAME);
    
        if (!dexInternalStoragePath.exists()) {
            mProgressDialog = ProgressDialog.show(this,
                    getResources().getString(R.string.diag_title),
                    getResources().getString(R.string.diag_message), true, false);
            // Perform the file copying in an AsyncTask.
            (new PrepareDexTask()).execute(dexInternalStoragePath);
        } else {
            btn_class.setEnabled(true);
        }
    
        btn_class.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
    
                // Initialize the class loader with the secondary dex file.
                DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
                        optimizedDexOutputPath.getAbsolutePath(),
                        null,
                        getClassLoader());
    
                Class<Object> libProviderClazz = null;
    
                try {
                    libProviderClazz = (Class<Object>) cl.loadClass("com.fsd.toastlib.ToastActivity");
    
                    // CHANGED: THIS STARTS PROPERLY YOUR ACTIVITY ONCE THE CLASS LOADED
                    final Intent intent = new Intent(getApplicationContext(), libProviderClazz);
                    startActivityForResult(intent, 0);
                } catch (ClassNotFoundException e) {
                    // handle that Exception properly here
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    
    // File I/O code to copy the secondary dex file from asset resource to internal storage.
    private boolean prepareDex(File dexInternalStoragePath) {
        BufferedInputStream bis = null;
        OutputStream dexWriter = null;
    
        try {
            //bis = new BufferedInputStream(getAssets().open(SECONDARY_DEX_NAME));
            Log.i("customClassLoader", "test");
            File extDir = Environment.getExternalStorageDirectory();
            Log.i("customClassLoader", extDir.getAbsolutePath());
            File dexExternalStorage = new File(extDir, SECONDARY_DEX_NAME);
            Log.i("customClassLoader", dexExternalStorage.getAbsolutePath());
            bis = new BufferedInputStream(new FileInputStream(dexExternalStorage));
            dexWriter = new BufferedOutputStream(new FileOutputStream(dexInternalStoragePath));
            byte[] buf = new byte[BUF_SIZE];
            int len;
            while((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
                dexWriter.write(buf, 0, len);
            }
            dexWriter.close();
            bis.close();
            return true;
        } catch (IOException e) {
            if (dexWriter != null) {
                try {
                    dexWriter.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            return false;
        }
    }
    
    private class PrepareDexTask extends AsyncTask<File, Void, Boolean> {
    
        @Override
        protected void onCancelled() {
            super.onCancelled();
            if (mProgressDialog != null) mProgressDialog.cancel();
        }
    
        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            if (mProgressDialog != null) mProgressDialog.cancel();
        }
    
        @Override
        protected Boolean doInBackground(File... dexInternalStoragePaths) {
            prepareDex(dexInternalStoragePaths[0]);
            return null;
        }
    }
    }
    

    (Library) com.fsd.toastlib.ToastActivity.java

    public class ToastActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_toast);
    
        Button main = (Button) findViewById(R.id.btn_main);
    
        main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TextView teks = (TextView) findViewById(R.id.lib_text);
                teks.setText("Start");
    
                Toast.makeText(getApplicationContext(), "Game is running", Toast.LENGTH_LONG).show();
            }
        });
    }
    }
    
  2. How to generate jar that include the res folder, currently it only contains the java file. Then, can I access it directly like my code in ToastActivity.java, ex:

    TextView teks = (TextView) findViewById(R.id.lib_text);

Code that I used to generate jar file

(Library) build.gradle

import org.apache.tools.ant.taskdefs.condition.Os

apply plugin: 'com.android.library'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
}

// Define some tasks which are used in the build process
task copyClasses(type: Copy) { // Copy the assembled *.class files for only the current namespace into a new directory
    // get directory for current namespace (PLUGIN_NAMESPACE = 'com.example.toastlib')
    String PLUGIN_NAMESPACE = "com.fsd.toastlib"
    def namespacePath = PLUGIN_NAMESPACE.replaceAll("\\.","/")
    // set source and destination directories
    from "build/intermediates/classes/release/${namespacePath}/"
    into "build/intermediates/dex/${namespacePath}/"

    // exclude classes which don't have a corresponding .java entry in the source directory
    def remExt = { name -> name.lastIndexOf('.').with {it != -1 ? name[0..<it] : name} }
    eachFile {details ->
        def thisFile = new File("${projectDir}/src/main/java/${namespacePath}/", remExt(details.name)+".java")
        if (!(thisFile.exists())) {
            details.exclude()
        }
    }
}

task assembleExternalJar << {
    // Get the location of the Android SDK
    ext.androidSdkDir = System.env.ANDROID_HOME
    if(androidSdkDir == null) {
        Properties localProps = new Properties()
        localProps.load(new FileInputStream(file('local.properties')))
        ext.androidSdkDir = localProps['sdk.dir']
    }
    // Make sure no existing jar file exists as this will cause dx to fail
    new File("${buildDir}/intermediates/dex/com.fsd.toastlib.jar").delete();
    // Use command line dx utility to convert *.class files into classes.dex inside jar archive
    String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? '.bat' : ''
    exec {
        commandLine "${androidSdkDir}\\build-tools\\26.0.1\\dx${cmdExt}", '--dex',
                "--output=${buildDir}/intermediates/dex/com.fsd.toastlib.jar",
                "${buildDir}/intermediates/dex/"
    }
    copyJarToOutputs.execute()
}

task copyJarToOutputs(type: Copy) {
    // Copy the built jar archive to the outputs folder
    from 'build/intermediates/dex/'
    into 'build/outputs/'
    include '*.jar'
}

// Set the dependencies of the build tasks so that assembleExternalJar does a complete build
copyClasses.dependsOn(assemble)
assembleExternalJar.dependsOn(copyClasses)

0

There are 0 answers