主要是原生实现的,然后和js进行一个通信。
1,首先在android/app/src/main/AndroidManifest.xml,appapplication标签里添加
//权限需要这几个
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
.........
//添加一下代码
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.project.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths_public" />
</provider>
2,创建rn类文件
// CustomPackage.java
package com.project; // 包名
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomPackage implements ReactPackage {
private List<NativeModule> nativeModuleList;
@NonNull
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@NonNull
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> nativeModuleList = new ArrayList<>();
nativeModuleList.add(new MyNativeModule(reactContext));
return nativeModuleList;
}
}
3,创建桥文件,并声明原生下载方法download,接收一个下载链接
// MyNativeModule.java
package com.project; // 包名
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.connection.RealCall;
public class MyNativeModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;
public MyNativeModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
@Override
public String getName() {
return "MyNativeModule"; // 此处为rn里调用原生模块时的名字
}
@ReactMethod
public void openNativePage() {
Log.e("MyNativeModule", "openNativePage: download");
}
@ReactMethod
void download(String url) {
Log.e("download", url);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.e("download", e.toString());
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
WriteFileUtils.writeFile(response, new IDownloadListener() {
@Override
public void start(long max) {
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("startDownProgressNum", max + "");
}
@Override
public void loading(long progress) {
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("downProgressNum", progress+"");
}
@Override
public void loadFail(String msg) {
Log.e("download", msg);
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("downProgressNum", "-1");
}
@Override
public void complete(Uri fileUri) {
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("downProgressNum", "100");
String path = null;
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
// 如果是Android 10以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
path = MainApplication.getInstance().getExternalCacheDir().getPath() + "/temp.apk";
} else {
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + "/temp.apk";
}
WriteFileUtils.installApk(path,getCurrentActivity());
}
});
}
});
}
}
4,创建下载类IDownloadListener.java和WriteFileUtils.java
package com.project;
import android.net.Uri;
public interface IDownloadListener {
void start(long max);
void loading(long progress );
void loadFail(String msg);
void complete(Uri fileUri);
}
package com.project;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.core.content.FileProvider;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import okhttp3.Response;
public class WriteFileUtils {
/*
*
* val imageCollection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val imageDetails = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "test")
put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg")
}
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val saveUri = resolver.insert(imageCollection, imageDetails)
val outputStream: OutputStream? = contentResolver.openOutputStream(saveUri!!)
outputStream?.let {
it.write(bytes)
it.close()
}
* */
public static void writeFile(Response response, IDownloadListener downloader) {
try {
InputStream is = response.body().byteStream();
OutputStream fos;
Uri fileUri = null;
File file;
//获取文件总长度
long totalLength = response.body().contentLength();
downloader.start(totalLength);
String downloadUrl = MainApplication.getInstance().getExternalFilesDir(null).getAbsolutePath();
// 下载文件的路径
String path = null;
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
// 如果是Android 10以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
path = MainApplication.getInstance().getExternalCacheDir().getPath();
} else {
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
file = new File(path
+ "/temp.apk");
} else {
file = new File(path, "temp.apk");
}
fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
int progress = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
progress += len;
//此处进行更新操作
//len即可理解为已下载的字节数
downloader.loading(progress);
}
fos.flush();
fos.close();
bis.close();
is.close();
downloader.complete(fileUri);
//此处就代表更新结束
} catch (Exception e) {
downloader.loadFail(e.getLocalizedMessage());
}
}
public static void installApk(String path, Activity activity) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(path);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String authority = activity.getPackageName() + ".fileprovider";
Uri fileUri = FileProvider.getUriForFile(activity.getApplicationContext(), authority, file);
intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
activity.startActivityForResult(intent, 0);
} catch (Exception e) {
Log.e("errer", e.toString());
}
}
}
5,在/android/app/src/main/java/com/project2/MainApplication.java下导入类
//import com.project.CustomPackage;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.internal.http.HttpHeaders;
......
.......
//修改getPackages,和onCreate方法
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new CustomPackage());
return packages;
}
public void onCreate() {
super.onCreate();
instance = this;
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
List<ReactPackage> list =getReactNativeHost().getReactInstanceManager().getPackages();
list.add(new CustomPackage());
}
.....
//增加以下代码
private static MainApplication instance;
public synchronized static MainApplication getInstance(){
if(null==instance){
instance = new MainApplication();
}
return instance;
}
6,增加这个文件android/app/src/main/res/xml/file_paths_public.xml
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2017 Zhihu Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<paths>
<root-path
name="camera_photos"
path="" />
<!-- 这个是保存拍照图片的路径,必须配置。 -->
<external-files-path
name="images"
path="Pictures" />
<external-path
name="external-path"
path="." />
<cache-path
name="my_cache"
path="/" />
<external-cache-path
name="my_ex_cache"
path="/" />
<external-files-path
name="sdcard"
path="" />
</paths>
最后在rn里调用就可以了
useEffect(() => {
// 注册安卓原生下载通信事件
DeviceEventEmitter.addListener('startDownProgressNum', (max) => {
console.log('开始下载');
setadPackTotalSize(Number(max))
});
DeviceEventEmitter.addListener('downProgressNum', (msg) => {
if (msg == '-1') {
console.log('下载失败');
} else if (msg == '100') {
console.log('下载完成');
setDownOver(true)
setAdDownModalShow(false)
} else if (msg != undefined) {
if (timerLock.current) return;
console.log('ddddd', Number(msg));
setProgressNum(Number(msg))
timerLock.current = true
setTimeout(() => {
// console.log(333);
timerLock.current = false
}, 1000)
}
});
return () => {
DeviceEventEmitter.removeAllListeners('startDownProgressNum');
DeviceEventEmitter.removeAllListeners('downProgressNum');
}
}, [])
// 调用下载
NativeModules.MyNativeModule.download(downUrl)