什么是 NDK?
- NDK - Native Development Kit
- NDK是Android的一个开发工具包,可通过 NDK ,使用 JNI 与 Native(C,C++)代码交互。
- 在 [[Android-SDK]] 开发环境中,通过 NDK 实现 JNI (Java Native Interface) 的功能。
Android的UI线程
UI线程就是主线程.
不要在UI线程执行任何耗时操作.
必须在UI线程访问UI控件.
C++/C#/Java之间交互,牵扯到状态的更改,都要在UI线程中执行。
Unity和Android互操作
- Unity和 [[Android-SDK]] 互操作的方法。
Unity调用Android方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 获取当前的Activity
javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");
// 获取特定的Activity
mainActivity = new AndroidJavaClass("com.feamber.sdk.MainActivity");
// 调用Static方法
mainActivity.CallStatic("ShowBanner", "Main"); // 方法名后为参数
int value = mainActivity.CallStatic<int>("GetInt", key); // 泛型参数为返回类型
// 支持方法重载,下面两个可以同时存在
mainActivity.CallStatic("TASDKUserAdd", string, float);
mainActivity.CallStatic("TASDKUserAdd", Map<string,string);
Android调用Unity方法
1
2
UnityPlayer.UnitySendMessage("AdsHandler", "OnRewardVideoWatched", "");
// 场景中的游戏对象GameObject 方法名 参数
Unity中通过Dictionary创建Java的HashMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static AndroidJavaObject DicToMap(Dictionary<string, string> dictionary)
{
if(dictionary == null)
{
return null;
}
AndroidJavaObject map = new AndroidJavaObject("java.util.HashMap");
foreach(KeyValuePair<string, string> pair in dictionary)
{
map.Call<string>("put", pair.Key, pair.Value);
}
return map;
}
AndroidJavaObject map = DicToMap(param);
mainActivity.CallStatic("CustomEventWithParams", eventId, map);
启动和关闭Activity
1
2
3
4
5
// 启动Activity
Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
startActivity(intent);
// 关闭Activity
finish();
显示对话框(Dialog)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
private boolean showPrivacyDialog() {
Log.d(TAG, "showPrivacyDialog: ");
if (SpUtil.getInstance().getBoolean(Constant.PRIVACY_PASS, false)) {
return false;
}
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle("温馨提示");
builder.setMessage(R.string.privacyDesc);
builder.setNegativeButton("不同意", null);
builder.setPositiveButton("同意", null);
builder.setCancelable(false);
AlertDialog dialog = builder.create();
// 设置按钮颜色
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.GRAY);
}
});
dialog.show();
// show之后设置回调,防止点击按钮之后Dialog自动关闭
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "onClick: negative");
ToastHelper.showTextBottom(mActivity, "点击同意才可以继续游戏");
}
});
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "onClick: positive");
VivoUnionSDK.onPrivacyAgreed(mActivity);
SpUtil.getInstance().putBoolean(Constant.PRIVACY_PASS, true);
applyPermission();
dialog.dismiss();
}
});
// 设置使链接可点击
((TextView)dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
return true;
}
Android存档系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package com.mobilekit.utils;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Map;
public class SpUtil {
private Context mContext;
private SharedPreferences mSP;
// 单例,延迟初始化
private static class Holder {
private static SpUtil sInstance = new SpUtil();
}
private SpUtil() {}
public static SpUtil getInstance() {
return Holder.sInstance;
}
public void init(Context context)
{
mContext = context;
mSP = mContext.getSharedPreferences("mobileKit", Context.MODE_PRIVATE);
}
public void putBoolean(String key, boolean value) {
mSP.edit().putBoolean(key, value).apply();
}
public boolean getBoolean(String key, boolean defaultValue) {
return mSP.getBoolean(key, defaultValue);
}
/**
* 保存数据
*/
public static void put(Context context, String fileName, String key, Object obj){
SharedPreferences sp = context.getSharedPreferences(fileName, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (obj instanceof Boolean) {
editor.putBoolean(key, (Boolean) obj);
} else if (obj instanceof Float) {
editor.putFloat(key, (Float) obj);
} else if (obj instanceof Integer) {
editor.putInt(key, (Integer) obj);
} else if (obj instanceof Long) {
editor.putLong(key, (Long) obj);
} else {
editor.putString(key, (String) obj);
}
//apply()是异步写入数据
editor.apply();
//commit()是同步写入数据
//editor.commit();
}
/**
* 获取指定数据
*/
public static Object get(Context context, String fileName, String key, Object defaultObj) {
SharedPreferences sp = context.getSharedPreferences(fileName, context.MODE_PRIVATE);
if (defaultObj instanceof Boolean) {
return sp.getBoolean(key, (Boolean) defaultObj);
} else if (defaultObj instanceof Float) {
return sp.getFloat(key, (Float) defaultObj);
} else if (defaultObj instanceof Integer) {
return sp.getInt(key, (Integer) defaultObj);
} else if (defaultObj instanceof Long) {
return sp.getLong(key, (Long) defaultObj);
} else if (defaultObj instanceof String) {
return sp.getString(key, (String) defaultObj);
}
return null;
}
/**
* 删除指定数据
*/
public static void remove(Context context, String fileName, String key) {
SharedPreferences sp = context.getSharedPreferences(fileName, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
//editor.commit();
editor.apply();
}
/**
* 返回所有键值对
*/
public static Map<String, ?> getAll(Context context, String fileName) {
SharedPreferences sp = context.getSharedPreferences(fileName, context.MODE_PRIVATE);
Map<String, ?> map = sp.getAll();
return map;
}
/**
* 删除所有数据
*/
public static void clear(Context context, String fileName) {
SharedPreferences sp = context.getSharedPreferences(fileName, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
//editor.commit();
editor.apply();
}
/**
* 检查key对应的数据是否存在
*/
public static boolean contains(Context context, String fileName, String key) {
SharedPreferences sp = context.getSharedPreferences(fileName, context.MODE_PRIVATE);
return sp.contains(key);
}
}
显示Toast提示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.mobilekit.utils;
import android.content.Context;
import android.view.Gravity;
import android.widget.Toast;
public class ToastHelper {
private static String oldMsg;
private static long displayTime;
public static void showTextBottom(Context context, String msg) {
if (!msg.equals(oldMsg) || System.currentTimeMillis() - displayTime > 2000) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
displayTime = System.currentTimeMillis();
}
}
public static void showTextMiddle(Context context, String msg) {
if (!msg.equals(oldMsg) || System.currentTimeMillis() - displayTime > 2000) {
Toast toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
}
读取Xml内容
- 一个在Android-Activity中读取 [[XML]] 值的Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
// string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="privacyDesc">欢迎进入游戏!为了保护您的隐私和使用安全,请您务必仔细阅读我们的
<a href="https://crazycooking.vrcoolgame.com/vrcoolgame/conditioncn.html">《用户协议》</a>
和
<a href="https://crazycooking.vrcoolgame.com/vrcoolgame/privacycn.html">《隐私政策》</a>。
我们希望通过这些条款,向您说明在使用我们的产品时,我们如何使用、保存和管理这些信息。
您点击同意的行为即表示您已经阅读完毕并同意上述协议和政策种的全部内容。
如您有任何问题、意见或投诉,请与我们联系。</string>
</resources>
1
2
3
4
// 取得resID
builder.setMessage(R.string.privacyDesc);
// 取得string值
Html.fromHtml(getString(R.string.privacyDesc));