预置文件到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"/>
    <application
        android: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";
	
	@Override
	public 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);
	}
	
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
	
		debug("CopyService onCreate");
		mCopyReceiver.doCopyStuff(this);
	}
	
	
	@Override
	public void onStart(Intent intent, int startId) {
		// TODO Auto-generated method stub
		super.onStart(intent, startId);
	}
	
	@Override
	public 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 block
			e.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 block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.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
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值