以下代码为简化思路,测试模为Qwen2-0.5B-Instruct-MNN,模型文件放在
/storage/emulated/0/modelscope/Qwen2-0.5B-Instruct-MNN
模型文件不要用git clone获取到的,必须是用魔搭、魔乐sdk获取到的,或者从之前从MNN下载缓存中拷贝过来的
测试时需要先获取管理所有文件的权限,然后清退并重新打开MNN,等待5秒后跳转ChatActivity.java并自动加载模型
更多mnnchat实现关注我的github
https://github.com/AIddlx/ddlx-MnnLlmChat-OpenaiAPI
此次实现的java文件在
ddlx-MnnLlmChat-OpenaiAPI
我的b站
https://space.bilibili.com/3493283904883140
以下是此次的代码
以下是app\src\main\AndroidManifest.xml需要新增的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
以下是MainActivity的代码
app\src\main\java\com\alibaba\mnnllm\android\MainActivity.java
// Created by ruoyi.sjd on 2024/12/25.
// Copyright (c) 2024 Alibaba Group Holding Limited All rights reserved.
package com.alibaba.mnnllm.android;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import com.alibaba.mls.api.download.ModelDownloadManager;
import com.alibaba.mnnllm.android.chat.ChatActivity;
import com.alibaba.mnnllm.android.history.ChatHistoryFragment;
import com.alibaba.mnnllm.android.modelist.ModelListFragment;
import com.alibaba.mnnllm.android.settings.MainSettings;
import com.alibaba.mnnllm.android.update.UpdateChecker;
import com.alibaba.mnnllm.android.utils.GithubUtils;
import com.alibaba.mnnllm.android.utils.ModelUtils;
import com.techiness.progressdialoglibrary.ProgressDialog;
import java.io.File;
import java.util.Objects;
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
private ProgressDialog progressDialog;
private DrawerLayout drawerLayout;
private ActionBarDrawerToggle toggle;
private ModelListFragment modelListFragment;
private ChatHistoryFragment chatHistoryFragment;
private UpdateChecker updateChecker;
/**
* 用于启动权限请求的 ActivityResultLauncher
* 实现日期:2025年4月21日 创作者:ddlx
*/
private ActivityResultLauncher<String> requestPermissionLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
drawerLayout = findViewById(R.id.drawer_layout);
updateChecker = new UpdateChecker(this);
updateChecker.checkForUpdates(this, false);
// Set up ActionBar toggle
toggle = new ActionBarDrawerToggle(
this, drawerLayout,
toolbar,
R.string.nav_open,
R.string.nav_close);
drawerLayout.addDrawerListener(toggle);
toggle.syncState();
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_fragment_container,
getModelListFragment())
.commit();
getSupportFragmentManager().beginTransaction()
.replace(R.id.history_fragment_container,
getChatHistoryFragment())
.commit();
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START);
} else {
finish();
}
}
});
setSupportActionBar(toolbar);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
// 初始化权限请求启动器
//实现日期:2025年4月21日 创作者:ddlx
initializePermissionLauncher();
// 如果需要,请求存储权限
//实现日期:2025年4月21日 创作者:ddlx
requestStoragePermissionIfNeeded();
// 延迟并运行模型
// 实现日期:2025年4月21日 创作者:ddlx
delayAndRunModel();
}
private Fragment getModelListFragment() {
if (modelListFragment == null) {
modelListFragment = new ModelListFragment();
}
return modelListFragment;
}
private Fragment getChatHistoryFragment() {
if (chatHistoryFragment == null) {
chatHistoryFragment = new ChatHistoryFragment();
}
return chatHistoryFragment;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (toggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* 初始化权限请求发射器
*
* 使用 registerForActivityResult 方法注册一个新的 ActivityResultContracts.RequestPermission 类型的发射器。
* 当权限请求的结果返回时,会调用回调函数。如果权限未被授予,则显示一个 Toast 提示用户需要存储权限。
* 实现日期:2025年4月21日 创作者:ddlx
*/
private void initializePermissionLauncher() {
requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
isGranted -> {
if (!isGranted) {
Toast.makeText(this, "需要存储权限", Toast.LENGTH_LONG).show();
}
}
);
}
/**
* 根据系统版本请求存储权限
*
* @return 无返回值
* 实现日期:2025年4月21日 创作者:ddlx
*/
private void requestStoragePermissionIfNeeded() {
// 检查是否已经拥有管理外部存储的权限
if (Environment.isExternalStorageManager()) {
// 如果已经有权限,则记录日志并返回
Log.d(TAG, "已拥有 MANAGE_EXTERNAL_STORAGE 权限。");
return;
}
// 检查 Android 版本是否为 R (API 30) 或更高
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
// Android R 及以上版本,请求 MANAGE_EXTERNAL_STORAGE 权限
Log.d(TAG, "为 Android R+ 请求 MANAGE_EXTERNAL_STORAGE 权限");
try {
// 创建意图以请求所有文件访问权限
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
} catch (Exception e) {
// 如果启动 Activity 出错,记录错误日志
Log.e(TAG, "启动 MANAGE_ALL_FILES_ACCESS_PERMISSION activity 时出错", e);
// 尝试再次启动,不指定特定包
Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent); // Try again without specific package
}
} else {
// Android R 以下版本,请求传统的存储权限
// 检查 Android 版本是否为 Android 14 (API 34) 或更高
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// Android 14 及以上版本,请求 READ_MEDIA_IMAGES 权限
Log.d(TAG, "为 Android 14+ 请求 READ_MEDIA_IMAGES 权限");
requestPermissionLauncher.launch(Manifest.permission.READ_MEDIA_IMAGES);
} else {
// Android 14 以下版本,请求 READ_EXTERNAL_STORAGE 权限
Log.d(TAG, "为低于 Android 14 的版本请求 READ_EXTERNAL_STORAGE 权限");
requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE);
}
}
}
/**
* 延迟5秒后运行模型
*
* 该方法会创建一个 Handler 实例,并设置一个延迟5秒的 Runnable,当延迟时间到达后,将执行 runModel 方法。
* 实现日期:2025年4月21日 创作者:ddlx
*/
private void delayAndRunModel() {
// 创建一个 Handler 实例
Handler handler = new Handler(Looper.getMainLooper());
// 延迟5秒后执行 runModel 方法
handler.postDelayed(() -> runModel(null, null, null), 5000); // 5000 毫秒 = 5 秒
}
/**
* 运行模型
*
* @param destModelDir 目标模型目录
* @param modelId 模型ID
* @param sessionId 会话ID
* 实现日期:2025年4月21日 创作者:ddlx
*/
public void runModel(String destModelDir, String modelId, String sessionId) {
// 固定模型路径和 modelId
String absolutePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/modelscope/Qwen2-0.5B-Instruct-MNN";
modelId = "taobao-mnn/Qwen2-0.5B-Instruct-MNN";
destModelDir = absolutePath; // 使用固定路径
Log.d(TAG, "运行模型 destModelDir: " + destModelDir);
if (MainSettings.INSTANCE.isStopDownloadOnChatEnabled(this)) {
ModelDownloadManager.getInstance(this).pauseAllDownloads();
}
drawerLayout.close();
progressDialog = new ProgressDialog(this);
progressDialog.setMessage(getResources().getString(R.string.model_loading));
progressDialog.show();
// 移除扩散模型判断逻辑
String configFileName = "config.json";
String configFilePath = destModelDir + "/" + configFileName;
boolean configFileExists = new File(configFilePath).exists();
if (!configFileExists) {
Toast.makeText(this, getString(R.string.config_file_not_found, configFilePath), Toast.LENGTH_LONG).show();
progressDialog.dismiss();
return;
}
progressDialog.dismiss();
Intent intent = new Intent(this, ChatActivity.class);
intent.putExtra("chatSessionId", sessionId);
// 移除扩散模型相关的额外参数设置
intent.putExtra("configFilePath", configFilePath);
intent.putExtra("modelId", modelId);
intent.putExtra("modelName", "Qwen2-0.5B-Instruct-MNN");
startActivity(intent);
}
public void onStarProject(View view) {
GithubUtils.starProject(this);
}
public void onReportIssue(View view) {
GithubUtils.reportIssue(this);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == ModelDownloadManager.REQUEST_CODE_POST_NOTIFICATIONS) {
ModelDownloadManager.getInstance(this).startForegroundService();
}
}
public void checkForUpdate() {
updateChecker.checkForUpdates(this, true);
}
}