Browse Source

1.下载方式改为Okhttp

JaycePC 1 month ago
parent
commit
df3e482f69
1 changed files with 220 additions and 214 deletions
  1. 220 214
      app/src/main/java/xn/xxp/app/SyncFaceTool.java

+ 220 - 214
app/src/main/java/xn/xxp/app/SyncFaceTool.java

@@ -2,15 +2,10 @@ package xn.xxp.app;
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
-import android.app.DownloadManager;
-import android.content.Context;
-import android.net.Uri;
 import android.os.Environment;
-import android.os.FileObserver;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 import androidx.core.util.Pair;
 
@@ -32,15 +27,27 @@ import org.greenrobot.eventbus.ThreadMode;
 import org.json.JSONObject;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
+import java.util.Collections;
 import java.util.List;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import http.OkHttpUtils;
 import http.client.HttpTool;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
 import okhttp3.Response;
+import okio.BufferedSink;
+import okio.Okio;
 import xn.xxp.home.auth.ChoiceAuthActivity;
 import xn.xxp.home.setting.FaceListActivity;
 import xn.xxp.room.RoomTool;
@@ -50,16 +57,12 @@ import xn.xxp.room.dao.FaceDao;
 public enum SyncFaceTool {
     INSTANCE;
 
-    private DownloadManager downloadManager;
-    private long lastEventTime = 0;
-    private long lastBuildTime = 0;
+    private static final String TAG = "SyncFaceTool";
+    private static final int MAX_DOWNLOAD_THREADS = 4;
     private volatile boolean isBuildFaceEnd = false;
     private List<Face> faceList;
     public volatile boolean isSyncFace = false;
-
-    SyncFaceTool() {
-
-    }
+    private long lastBuildTime = 0;
 
     @WorkerThread
     public void doWork() {
@@ -67,247 +70,250 @@ public enum SyncFaceTool {
             LogUtils.e("该方法必须在子线程调用");
             return;
         }
-        Activity topActivity = ActivityUtils.getTopActivity();
-        if (topActivity instanceof ChoiceAuthActivity) {
-            LogUtils.d("人脸识别中,不去同步人脸!");
-            return;
-        }
-        if (topActivity instanceof FaceListActivity) {
-            LogUtils.d("正在查阅底库,不去同步人脸!");
-            return;
-        }
+
+        if (!checkActivityState()) return;
+
+
         if (isSyncFace) {
             LogUtils.d("正在同步人脸");
             return;
         }
-        if (null == downloadManager) {
-            downloadManager = (DownloadManager) Utils.getApp().getApplicationContext().getSystemService(Context.DOWNLOAD_SERVICE);
-        }
-        // 初始化
+
         isSyncFace = true;
         try {
-            // 同步人脸
             FaceUtils.getInstance().init(Utils.getApp());
             FaceConfig faceConfig = FaceUtils.getInstance().getFaceConfig();
             LogUtils.json("底库配置", faceConfig);
+
             EventBus.getDefault().register(this);
-            // http请求同步
-            List<Pair<String, String>> faceUrlList = getLabFaceList();
-            if (null != faceUrlList && !faceUrlList.isEmpty()) {
-                // 下载人脸
-                List<FaceBuildBean> faceBuildBeanList = downloadPicAndBuild(faceUrlList);
-                LogUtils.json("需要变更的人脸", faceBuildBeanList);
-                // 人脸建模
-                if (null != faceBuildBeanList && !faceBuildBeanList.isEmpty()) {
-                    FaceUtils.getInstance().faceBuild(faceBuildBeanList);
-                    lastBuildTime = System.currentTimeMillis();
-                    // 等待人脸建模
-                    while (!isBuildFaceEnd) {
-                        Thread.sleep(10);
-                        if (System.currentTimeMillis() - lastBuildTime > 2000) {
-                            isBuildFaceEnd = true;
-                        }
-                    }
-                }
-                LogUtils.json(RoomTool.getInstance().faceDao().getAll());
-            } else {
+
+            List<Pair<String, String>> faceUrlList = fetchLabFaceList();
+            if (faceUrlList == null || faceUrlList.isEmpty()) {
                 LogUtils.d("不需要下载变更人脸");
+                return;
+            }
+
+            List<FaceBuildBean> faceBuildBeanList = downloadFaceImages(faceUrlList);
+            if (faceBuildBeanList != null && !faceBuildBeanList.isEmpty()) {
+                processFaceBuilding(faceBuildBeanList);
             }
-            // 删除本地多余的人脸
-            delRedundantFace();
-            LogUtils.d("底库", FaceUtils.getInstance().getAllFaceId());
+
+            cleanRedundantFaces();
+            LogUtils.d("最终底库", FaceUtils.getInstance().getAllFaceId());
         } catch (Exception e) {
             LogUtils.e(Log.getStackTraceString(e));
         } finally {
-            try {
-                // 销毁
-                EventBus.getDefault().unregister(this);
-                FaceUtils.getInstance().destroy();
-            } catch (Exception e) {
-                LogUtils.e(Log.getStackTraceString(e));
+            cleanupResources();
+            isSyncFace = false;
+        }
+    }
+
+    private boolean checkActivityState() {
+        Activity topActivity = ActivityUtils.getTopActivity();
+        if (topActivity instanceof ChoiceAuthActivity) {
+            LogUtils.d("人脸识别中,不去同步人脸!");
+            return false;
+        }
+        if (topActivity instanceof FaceListActivity) {
+            LogUtils.d("正在查阅底库,不去同步人脸!");
+            return false;
+        }
+        return true;
+    }
+
+    private List<Pair<String, String>> fetchLabFaceList() {
+        try (Response response = HttpTool.getLabFaceList()) {
+            if (response == null || !response.isSuccessful()) return null;
+
+            JSONObject jsonObject = new JSONObject(response.body().string());
+            if (jsonObject.getInt("code") != 200) return null;
+
+            Type faceListType = new TypeToken<List<Face>>() {
+            }.getType();
+            faceList = GsonUtils.fromJson(jsonObject.getJSONArray("data").toString(), faceListType);
+            if (faceList == null || faceList.isEmpty()) return Collections.emptyList();
+
+            return filterNeedDownloadFaces();
+        } catch (Exception e) {
+            LogUtils.e(Log.getStackTraceString(e));
+            return null;
+        }
+    }
+
+    private List<Pair<String, String>> filterNeedDownloadFaces() {
+        List<Pair<String, String>> results = new ArrayList<>();
+        FaceDao faceDao = RoomTool.getInstance().faceDao();
+        List<String> existingFaceIds = FaceUtils.getInstance().getAllFaceId();
+
+        for (Face face : faceList) {
+            Face localFace = faceDao.getById(face.getUserId());
+            if (shouldDownloadFace(localFace, face, existingFaceIds)) {
+                results.add(Pair.create(
+                        String.valueOf(face.getUserId()),
+                        HttpTool.checkUrl(face.getFaceUrl())
+                ));
             }
         }
-        isSyncFace = false;
+        return results;
+    }
+
+    private boolean shouldDownloadFace(Face localFace, Face remoteFace, List<String> existingFaceIds) {
+        if (localFace == null) return true;
+        if (remoteFace.getLastUpdated() <= localFace.getLastUpdated()) return false;
+        return !existingFaceIds.contains(String.valueOf(remoteFace.getUserId()));
     }
 
-    private List<Pair<String, String>> getLabFaceList() {
+    private List<FaceBuildBean> downloadFaceImages(List<Pair<String, String>> faceUrlList) {
+        ExecutorService executor = Executors.newFixedThreadPool(MAX_DOWNLOAD_THREADS);
+        ConcurrentHashMap<String, File> downloadedFiles = new ConcurrentHashMap<>();
+        CountDownLatch latch = new CountDownLatch(faceUrlList.size());
+        AtomicInteger failureCount = new AtomicInteger(0);
+
         try {
-            Response response = HttpTool.getLabFaceList();
-            if (null != response && response.isSuccessful()) {
-                String json = response.body().string();
-                JSONObject jsonObject = new JSONObject(json);
-                int code = jsonObject.getInt("code");
-                if (200 == code) {
-                    Type faceListType = new TypeToken<List<Face>>() {
-                    }.getType();
-                    faceList = GsonUtils.fromJson(jsonObject.getJSONArray("data").toString(), faceListType);
-
-                    if (null != faceList && !faceList.isEmpty()) {
-                        FaceDao faceDao = RoomTool.getInstance().faceDao();
-                        @SuppressLint("SdCardPath") final String savePath = "/sdcard/facePic/";
-                        FileUtils.createOrExistsDir(savePath);
-                        // 1.id 2.url
-                        List<Pair<String, String>> faceUrlList = new ArrayList<>();
-                        List<String> allFaceIdList = FaceUtils.getInstance().getAllFaceId();
-                        for (int i = 0; i < faceList.size(); i++) {
-                            Face face = faceList.get(i);
-                            Face sqlFace = faceDao.getById(face.getUserId());
-                            if (null != sqlFace) {
-                                boolean sqlInDb = false;
-                                for (int j = 0; j < allFaceIdList.size(); j++) {
-                                    if (allFaceIdList.get(j).equals(String.valueOf(sqlFace.getUserId()))) {
-                                        sqlInDb = true;
-                                    }
-                                }
-
-                                if (sqlInDb && face.getLastUpdated() <= sqlFace.getLastUpdated()) {
-                                    continue;
-                                }
-                            }
-                            faceUrlList.add(Pair.create(String.valueOf(face.getUserId()), HttpTool.checkUrl(face.getFaceUrl())));
+            String downloadDir = createDownloadDirectory();
+
+            for (Pair<String, String> pair : faceUrlList) {
+                executor.submit(() -> {
+                    try {
+                        File outputFile = new File(downloadDir, pair.first + ".jpg");
+                        if (downloadImage(pair.second, outputFile)) {
+                            downloadedFiles.put(pair.first, outputFile);
                         }
-                        return faceUrlList;
-                    } else {
-                        return new ArrayList<>();
+                    } finally {
+                        latch.countDown();
                     }
-                }
+                });
             }
-        } catch (Exception e) {
-            LogUtils.e(Log.getStackTraceString(e));
-        }
-        return null;
-    }
 
-    private List<FaceBuildBean> downloadPicAndBuild(List<Pair<String, String>> faceUrlList) {
-        if (null == faceUrlList || faceUrlList.isEmpty()) {
+            latch.wait();
+            executor.shutdown();
+
+            if (failureCount.get() > 0) {
+                LogUtils.w("部分人脸下载失败,失败数量:" + failureCount.get());
+            }
+
+            return buildFaceBeans(downloadedFiles);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
             return null;
+        } finally {
+            executor.shutdownNow();
         }
-        try {
-            @SuppressLint("SdCardPath") final String filePathDir = "/sdcard/" + Environment.DIRECTORY_DOWNLOADS + "/facePic/";
-            FileUtils.createOrExistsDir(filePathDir);
-            FileUtils.deleteAllInDir(filePathDir);
-            List<FaceBuildBean> faceBuildBeanList = new ArrayList<>();
-            List<File> downloadList = new LinkedList<>();
-            for (int i = 0; i < faceUrlList.size(); i++) {
-                // 1.id 2.url
-                Pair<String, String> downloadPair = faceUrlList.get(i);
-                DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadPair.second));
-                request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "facePic/" + downloadPair.first + ".jpg");
-                request.setMimeType("image/jpeg");
-                request.setVisibleInDownloadsUi(false);
-                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
-                downloadManager.enqueue(request);
-                downloadList.add(new File(filePathDir + downloadPair.first + ".jpg"));
+    }
+
+    private boolean downloadImage(String url, File outputFile) {
+        Request request = new Request.Builder().url(url).build();
+        try (Response response = OkHttpUtils.client.newCall(request).execute()) {
+            if (!response.isSuccessful() || response.body() == null) {
+                LogUtils.w("下载失败: " + url);
+                return false;
             }
-            // 文件观察者
-            FileObserver fileObserver = getFileObserver(filePathDir, downloadList);
-            // 开始观察
-            fileObserver.startWatching();
-            lastEventTime = System.currentTimeMillis();
-            // 观察者无响应2秒后 认为下载结束
-            while (!downloadList.isEmpty()) {
-                Thread.sleep(1000);
-                if (System.currentTimeMillis() - lastEventTime > 2000) {
-                    downloadList.clear();
-                }
+
+            try (InputStream is = response.body().byteStream();
+                 BufferedSink sink = Okio.buffer(Okio.sink(outputFile))) {
+                sink.writeAll(Okio.source(is));
+                return true;
             }
-            // 停止观察
-            fileObserver.stopWatching();
-
-            // 同步人脸
-            List<File> facePicList = FileUtils.listFilesInDir(filePathDir);
-            if (!facePicList.isEmpty()) {
-                for (int i = 0; i < facePicList.size(); i++) {
-                    File file = facePicList.get(i);
-                    FaceBuildBean faceBuildBean = new FaceBuildBean();
-                    faceBuildBean.setPicPath(file.getAbsolutePath());
-                    faceBuildBean.setFaceUuid(file.getName().replace(".jpg", ""));
-                    faceBuildBeanList.add(faceBuildBean);
-                }
+        } catch (IOException e) {
+            LogUtils.e("下载异常: " + url, e);
+            return false;
+        }
+    }
+
+    @NonNull
+    private String createDownloadDirectory() {
+        @SuppressLint("SdCardPath")
+        String dirPath = Environment.getExternalStorageDirectory() + "/Download/facePic/";
+        FileUtils.createOrExistsDir(dirPath);
+        FileUtils.deleteAllInDir(dirPath);
+        return dirPath;
+    }
+
+    private List<FaceBuildBean> buildFaceBeans(ConcurrentHashMap<String, File> downloadedFiles) {
+        List<FaceBuildBean> beans = new ArrayList<>();
+        downloadedFiles.forEach((id, file) -> {
+            FaceBuildBean bean = new FaceBuildBean();
+            bean.setPicPath(file.getAbsolutePath());
+            bean.setFaceUuid(id);
+            beans.add(bean);
+        });
+        return beans;
+    }
+
+    private void processFaceBuilding(List<FaceBuildBean> faceBuildBeanList)
+            throws InterruptedException {
+
+        FaceUtils.getInstance().faceBuild(faceBuildBeanList);
+        lastBuildTime = System.currentTimeMillis();
+        isBuildFaceEnd = false;
+
+        while (!isBuildFaceEnd) {
+            Thread.sleep(10);
+            if (System.currentTimeMillis() - lastBuildTime > 2000) {
+                isBuildFaceEnd = true;
             }
-            return faceBuildBeanList;
-        } catch (Exception e) {
-            LogUtils.e(Log.getStackTraceString(e));
         }
-        return null;
     }
 
     @Subscribe(threadMode = ThreadMode.ASYNC)
-    public void onReceive(BuildModelEvent buildModelEvent) {
-        if (buildModelEvent.isDone()) {
-            List<FaceBuildBean> list = FaceUtils.getInstance().getFaceResult();
-            FaceDao faceDao = RoomTool.getInstance().faceDao();
-            if (list != null && !list.isEmpty()) {
-                for (FaceBuildBean faceBuildBean : list) {
-                    if (faceBuildBean.getErrCode() != 0) {
-                        LogUtils.e("建模失败", faceBuildBean.getFaceUuid(), faceBuildBean.getErrCode());
-                    } else {
-                        if (null != faceList && !faceList.isEmpty()) {
-                            long uuid = Long.parseLong(faceBuildBean.getFaceUuid());
-                            for (int i = 0; i < faceList.size(); i++) {
-                                Face face = faceList.get(i);
-                                if (face.getUserId() == uuid) {
-                                    faceDao.insertAll(face);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
+    public void onReceive(BuildModelEvent event) {
+        if (event.isDone()) {
+            processBuildResults();
             isBuildFaceEnd = true;
         }
         lastBuildTime = System.currentTimeMillis();
     }
 
-    private @NonNull FileObserver getFileObserver(String filePathDir, List<File> downloadList) {
-        return new FileObserver(filePathDir) {
-            final Map<String, Integer> fileStateMap = new LinkedHashMap<>();
-
-            @Override
-            public void onEvent(int event, @Nullable String path) {
-                lastEventTime = System.currentTimeMillis();
-                if (null != path) {
-                    if (event == CLOSE_WRITE) {
-                        Integer count = fileStateMap.get(path);
-                        if (null == count) {
-                            fileStateMap.put(path, 1);
-                        } else if (count >= 2) {
-                            downloadList.removeIf(file -> path.equals(file.getName()));
-                        } else {
-                            fileStateMap.put(path, count + 1);
-                        }
-                    }
-                }
+    private void processBuildResults() {
+        List<FaceBuildBean> results = FaceUtils.getInstance().getFaceResult();
+        if (results == null || results.isEmpty()) return;
+
+        FaceDao faceDao = RoomTool.getInstance().faceDao();
+        results.forEach(bean -> {
+            if (bean.getErrCode() != 0) {
+                LogUtils.e("建模失败", bean.getFaceUuid(), bean.getErrCode());
+            } else {
+                updateFaceDatabase(faceDao, bean.getFaceUuid());
+            }
+        });
+    }
+
+    private void updateFaceDatabase(FaceDao faceDao, String faceUuid) {
+        if (faceList == null) return;
+
+        long uuid = Long.parseLong(faceUuid);
+        for (Face face : faceList) {
+            if (face.getUserId() == uuid) {
+                faceDao.insertAll(face);
+                break;
             }
-        };
+        }
     }
 
-    /**
-     * 删除本地多余的人脸
-     */
-    private void delRedundantFace() {
-        List<String> allFaceIdList = FaceUtils.getInstance().getAllFaceId();
+    private void cleanRedundantFaces() {
+        List<String> existingIds = FaceUtils.getInstance().getAllFaceId();
+        if (existingIds == null || existingIds.isEmpty()) return;
+
         FaceDao faceDao = RoomTool.getInstance().faceDao();
-        if (faceList != null && allFaceIdList != null && !allFaceIdList.isEmpty()) {
-            if (faceList.isEmpty()) {
-                FaceUtils.getInstance().deleteAllFace();
-                faceDao.clear();
-            } else {
-                for (int i = 0; i < allFaceIdList.size(); i++) {
-                    String uuid = allFaceIdList.get(i);
-                    boolean hasFace = false;
-                    for (int j = 0; j < faceList.size(); j++) {
-                        if (faceList.get(j).getUserId() == Long.parseLong(uuid)) {
-                            hasFace = true;
-                            break;
-                        }
-                    }
-                    if (!hasFace) {
-                        FaceUtils.getInstance().deleteFace(uuid);
-                        faceDao.delete(Long.parseLong(uuid));
-                    }
-                }
+        existingIds.forEach(id -> {
+            if (!containsFace(id)) {
+                FaceUtils.getInstance().deleteFace(id);
+                faceDao.delete(Long.parseLong(id));
             }
+        });
+    }
+
+    private boolean containsFace(String faceId) {
+        return faceList.stream().anyMatch(f ->
+                String.valueOf(f.getUserId()).equals(faceId));
+    }
+
+    private void cleanupResources() {
+        try {
+            EventBus.getDefault().unregister(this);
+            FaceUtils.getInstance().destroy();
+        } catch (Exception e) {
+            LogUtils.e(Log.getStackTraceString(e));
         }
     }
-}
+}