本文主要是介绍预置文件到sdcard目录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
了解为什么不可以直接预置
因为sdcard是挂载之后出现的,没法在编译的时候直接添加内容
无法直接预置,那么用什么替代
找一个已知会在编译的时候,可以预置文件的位置,这里用的是system文件夹
路径:
\device\mediatek\common\mid\common\system
原理是什么?
开启一个复制的服务,让sdcard加载的时候,复制system目录下的文件到需要的位置
方法
1.将需要的文件放到system目录下,可以有多级目录,这里在定义位置的时候做出选择
我这里放置来3张图片到extra目录下
2.这里建立一个package,做类似预置应用的操作
这里取个名叫CopyMedia,可以随便取名,注意在加包的时候做对应的修改
看目录可以知道有3个大文件,最上面那个就是我们的项目的代码。类似写安卓项目,目录层级是一样的,jni就是java和c#的互相调用,mk文件是编译的关键,据说12变成了bp文件,这里是11.
mk文件可以理解为说明书,具体可以看
https://blog.csdn.net/hejnhong/article/details/120585740
直接上CopyMedia的代码
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.mid.copymedia"android:versionCode="1"android:versionName="1.0"android:sharedUserId="android.uid.system" ><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><applicationandroid:icon="@drawable/ic_launcher"android:label="@string/app_name" ><service android:name=".CopyService"android:directBootAware="true"><intent-filter ><action android:name="mid.intent.action.COPYMEDIA"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></service><receiver android:name=".CopyReceiver"android:directBootAware="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver></application></manifest>
可以看到这里面没有Activity,只有一个服务和一个广播接收者,说明什么,没有界面。
CopyReceiver.java
package com.mid.copymedia;import java.io.File;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.SystemProperties;public class CopyReceiver extends BroadcastReceiver {private String BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";private static final String SRC_S = "/system/extra";private static final String SRC_D = "/data/extra";private static final String DST_STR = "persist.sys.sd.defaultpath";private static final String DST = "sdcard";@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();boolean copyOk = SystemProperties.get("persist.sys.mid.cpok","fail").equals("ok");//CopyJni.isCopyDown();CopyUtil.debug("onReceive action " + action);if(action.equals(BOOT_COMPLETED)){if(!copyOk){Intent copyIntent = new Intent(context, CopyService.class);context.startService(copyIntent);}}}public void doCopyStuff(final Context mContext){CopyJni.startProcess(mContext, SystemProperties.get("ro.preinstall.canreset", "yes").equals("yes") ?SRC_S:SRC_D, DST);}}
这个类,做了啥,定义了一堆的常量,还拿到了一个系统值,先通过action来筛选服务,这里没有直接调用doCopyStuff方法,而是通过开启一个服务,让服务去调用这个方法,为什么?很显然,这种操作只有可能是耗时,不方便在这里调用,为什么服务可以?这个需要去看对服务的定义了,不展开描述
CopyService.java
package com.mid.copymedia;import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;public class CopyService extends Service { private CopyReceiver mCopyReceiver = new CopyReceiver();public static void debug(String msg){android.util.Log.d("xxczy","mid=>"+msg);}@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();debug("CopyService onCreate");mCopyReceiver.doCopyStuff(this);}@Overridepublic void onStart(Intent intent, int startId) {// TODO Auto-generated method stubsuper.onStart(intent, startId);}@Overridepublic void onDestroy() {debug("CopyService onDestroy");super.onDestroy();}}
回过头去看CopyJni.startProcess(),这里好像啥都没做,但不可能,不在src文件中具体实现,那么就是在jni了,直接打开,可以看到
JNIEXPORT void JNICALL Java_com_mid_copymedia_CopyJni_startProcess(JNIEnv *env, jclass cls, jobject ctx, jstring src, jstring dst)
{/**jclass FindClass(JNIEnv *env, const char *name);*/jclass CopyUtilClz = env->FindClass("com/mid/copymedia/CopyUtil");if(CopyUtilClz == NULL){LOGD("CopyJni_startProcess can't find CopyUtilClz \n");return;}/* jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);*/jmethodID doCopyMthdId = env->GetStaticMethodID(CopyUtilClz, "doCopy","(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V");/**jstring NewStringUTF(JNIEnv *env, const char *bytes);*/// jstring src = env->NewStringUTF("/system/vendor_prebuilt");// jstring dst = env->NewStringUTF("/storage/sdcard0/vendor_media");/*NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz,jmethodID methodID, ...);*/env->CallStaticVoidMethod(cls,doCopyMthdId,ctx,src,dst);
}
这一看,就有点懵,反手查资料,可以知道
FindClass 该函数用于加载本地定义的类。它将搜索由CLASSPATH 环境变量为具有指定名称的类所指定的目录和 zip文件
CallStaticVoidMethod 调用静态方法
资料链接:
https://blog.csdn.net/prike/article/details/72790351?utm_source=blogkpcl7
嗯,可以看到又回去了,直接的意思就是把CopyUtil.java的静态方法全部调用了
CopyUtil.java
package com.mid.copymedia;import java.io.File;
import java.io.IOException;import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.SystemProperties;public class CopyUtil {public static void debug(String msg){android.util.Log.d("xxczy","mid=>"+msg);}public static String getRealPath(File file){try {debug("getRealPath getCanonicalPath="+file.getCanonicalPath()+" absPath="+file.getAbsolutePath());if(!file.getCanonicalPath().equals(file.getAbsolutePath())){return getRealPath(new File(file.getCanonicalPath()));}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return file.getAbsolutePath();}public static void doCopy(final Context ctx , final String SRC, final String DST){boolean copyOk = SystemProperties.get("persist.sys.mid.cpok","fail").equals("ok");//CopyJni.isCopyDown();debug("doCopyStuff copy "+ copyOk);if(!copyOk){new Thread(){public void run() {File src = new File(SRC);File dst = new File(DST);String dstPath = dst.getAbsolutePath();String dstCanoinacal = dstPath;debug("dstPath="+dstPath);dstCanoinacal = getRealPath(dst);debug("dstCanoinacal="+dstCanoinacal);dst = new File(dstCanoinacal);boolean ret = true;if(!dst.exists()){ret = dst.mkdirs();}if(!ret){debug("mkdir error return now "+dstCanoinacal);return;}CopyJni.doSomething(src, dst);CopyJni.copyDown(ctx, dstPath);};}.start();}}public static void updateMedia(Context context, String filename){ MediaScannerConnection.scanFile(context, new String[] { filename }, null, new MediaScannerConnection.OnScanCompletedListener() { public void onScanCompleted(String path, Uri uri) { debug("Scanned " + path + ":"); debug("-> uri=" + uri); } }); } }
很显然这里做了3件事:
1.拿具体路径
2.复制文件
3.跟新数据
直接看复制文件
嗯,前面一看就知道,就是对路径的是否存在的判断,没有就创建对应的文件夹,后面又调用了**CopyJni.doSomething(src, dst);和CopyJni.copyDown(ctx, dstPath);**方法
/** Class: com_mid_copymedia_CopyJni* Method: doSomething* Signature: (Ljava/io/File;Ljava/io/File;)V*/
JNIEXPORT void JNICALL Java_com_mid_copymedia_CopyJni_doSomething(JNIEnv *env, jclass cls, jobject srcFile, jobject dstFile)
{jclass FileUtilClz = env->FindClass("com/mid/copymedia/FileUtil");if(FileUtilClz == NULL){LOGD("CopyJni_doSomethind can't find FileUtilClz \n");return;}jmethodID copyFolderId = env->GetStaticMethodID(FileUtilClz, "copyFolder","(Ljava/io/File;Ljava/io/File;)V");env->CallStaticVoidMethod(cls,copyFolderId,srcFile,dstFile);}
/** Class: com_mid_copymedia_CopyJni* Method: copyDown* Signature: (Landroid/content/Context;Ljava/lang/String;)V*/
JNIEXPORT void JNICALL Java_com_mid_copymedia_CopyJni_copyDown(JNIEnv *, jclass, jobject, jstring);
嗯,感觉是吃太饱了,直接看FileUtil.java的代码
FileUtil.java
package com.mid.copymedia;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import android.util.Log;public class FileUtil {private static void debug(String msg) {android.util.Log.d("xxczy", "mid=>" + msg);}public static void copyFolder(File src, File dst) {debug("copyFolder " + src.getAbsolutePath() + " to "+ dst.getAbsolutePath());long startTime = System.currentTimeMillis();debug("src.isDir " + src.isDirectory() + " dst.isDir "+ dst.isDirectory());if (src.isDirectory() && dst.isDirectory()) {File[] srcFiles = src.listFiles();debug("srcFiles.len " + srcFiles.length);if (srcFiles != null) {File toFile = null;for (int i = 0; i < srcFiles.length; i++) {toFile = new File(dst, new String(srcFiles[i].getName().getBytes()));if (srcFiles[i].isDirectory()) {if (!toFile.exists()) {if (!toFile.mkdirs()) {debug("mkdirs failed "+ toFile.getAbsolutePath());continue;}}copyFolder(srcFiles[i], toFile);} else {copyfile(srcFiles[i], toFile, true);}}}}long endTime = System.currentTimeMillis();long totalMs = (endTime - startTime) / 1000;debug("totalTime = " + totalMs / 1000 + "s " + totalMs % 1000 + " ms");}public static void copyfile(File fromFile, File toFile, Boolean rewrite) {debug("start copy " + new String(fromFile.getAbsolutePath().getBytes())+ " to " + toFile.getAbsolutePath());long startTime = System.currentTimeMillis();if (!fromFile.exists()) {return;}if (!fromFile.isFile()) {return;}if (!fromFile.canRead()) {return;}if (!toFile.getParentFile().exists()) {toFile.getParentFile().mkdirs();}if (toFile.exists() && rewrite) {toFile.delete();}debug("check permission ok !");try {FileInputStream fosfrom = new FileInputStream(fromFile);FileOutputStream fosto = new FileOutputStream(toFile);byte[] bt = new byte[1024 * 128];int c;while ((c = fosfrom.read(bt)) > 0) {fosto.write(bt, 0, c);}fosfrom.close();fosto.close();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}long endTime = System.currentTimeMillis();long totalMs = (endTime - startTime) / 1000;debug("end copy file time = " + totalMs / 1000 + "s " + totalMs % 1000+ " ms");}public static void delete(File file) {if (file.isFile()) {file.delete();return;}if (file.isDirectory()) {File[] childFiles = file.listFiles();if (childFiles == null || childFiles.length == 0) {file.delete();return;}for (int i = 0; i < childFiles.length; i++) {delete(childFiles[i]);}file.delete();}}}
这里才是关键,用流去读取和写入文件
看过全部代码就知道了,有很多是多余的,就是个人对安卓系统开发的了解还较为浅,没有做实际修改
具体文件:
https://www.aliyundrive.com/s/aX6QJNadRbf
最后别忘了在\device\mediatek\system\common\device.mk中添加这个包名
PRODUCT_PACKAGES += CopyMedia
这篇关于预置文件到sdcard目录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!