ANDROID January 29, 2020

安卓开发入门

Words count 138k Reading time 2:06 Read count 0

一、准备工作

1、android studio +自带模拟器(或者真机root)

2、frida(一个hook神器)

3、jeb(反编译利器)

4、java环境变量jdk的配置(谷歌)

5、gradle安装

6、ndk的配置与安装

二、开始编程

1、程序基础

1
2
3
4
5
6
7
8
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//以什么样的界面显示出来的调用函数
}
}

所有的操作都是在oncreate中实现,这是定义一个大框架

2、日志查看与调试

1
Log.e("flag",flag);

在日志logcat打印出标签为flag的信息,很好用的调试小技巧,不同字母表示不同的日志信息

3、安卓元件的创建与控制

创建直接拖拽那个组件即可,想要控制时需要编程:

1
2
3
4
5
6
7
8
9
10
11
final TextView text = findViewById(R.id.textView);
text.setText(“123”);//通过id去识别控制
Button button2 = findViewById(R.id.button);
button2.setOnClickListener(new View.OnClickListener() {
@Override//创建一个监听事件
public void onClick(View v) {
String flag = "138193670177974882514318186152745798548673891499230311600";
text.setText(flag);
Log.e("flag",flag);
}
});

4、弹窗的创建

1
2
3
4
5
6
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(MainActivity.this);
alertDialog1.setTitle("恭喜!!");//标题
alertDialog1.setMessage("登录成功");//内容
alertDialog1.setIcon(R.mipmap.ic_launcher);//图标
alertDialog1.create();
alertDialog1.show();

5、页面之间的跳转设置,在java的包下面,新建hhh类,构建好代码,下面是在Mainactivity中的代码

1
2
3
Intent h = new Intent(MainActivity.this,hhh.class);//当前界面指针+要跳转的类
h.putExtra("name",b);//将数据通过标签映射进行存储
startActivity(h);//开始跳转活动

下面是hhh中的代码:

1
2
Intent intent = getIntent();//连接
String n = intent.getStringExtra("name");//获取标签中的值

最后要在manifests的application中声明下activity即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.love">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".hhh" >
</activity>
</application>

</manifest>

6、.so库文件的书写

下载ndk并且配置环境变量

直接在android studio中安装ndk,然后确定ndk的安装路径后,配置环境变量:

1
2
3
4
vim ~/.bash_profile
export ANDROID_HOME=~/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/ndk/21.0.6113669
source .bash_profile

检查时就输入ndk-build有回显即表示安装成功。

比如在Mainactivity中定义一个方法:

1
public native String getname();

native方法只能通过.so库来调用,切换到Terminal,进入到该工程的 java 目录下(如下图所示),然后输入

javah -jni 包名.类名(如下图所示)

1
v1ct0r@victordeMacBook-Pro java % javah -jni com.example.love.MainActivity //只是用来参考的

生成.h文件,参考作用而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_love_MainActivity */

#ifndef _Included_com_example_love_MainActivity
#define _Included_com_example_love_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_love_MainActivity
* Method: getname
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_love_MainActivity_getname
(JNIEnv *, jobject);//关键参考

#ifdef __cplusplus
}
#endif
#endif

编写.c文件

这时我们切换到Project,然后在app目录下新建jni文件夹,并在里面建立一个demo.c的c文件(如下图所示)。

1
2
3
4
5
6
7
8
9
10
#include <string.h>
#include <jni.h>
jstring Java_com_example_love_MainActivity_getname(JNIEnv *env, jobject thiz)
{
return (*env)->NewStringUTF(env, "I`m Str !");
}
jstring Java_com_example_love_hhh_getpwd(JNIEnv *env, jobject thiz)
{
return (*env)->NewStringUTF(env, "12345678");
}

知道一次以后都知道库函数怎么写了,要让哪个java类使用就要把类名写上,如上;

书写Android.mk文件,固定格式的

1
2
3
4
5
6
7
8
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := demo //要生成的so库的名称,但实际为libdemo.so
LOCAL_SRC_FILES := demo.c //要使用的文件,刚才编写的demo.c文件

include $(BUILD_SHARED_LIBRARY)

生成so文件

在控制台中,进入到工程的app目录下,然后输入ndk-build(如下所示),不出问题即可编译成功。

编译完成后刷新工程,可以看到在app目录下生成的libs和obj文件夹,其中libs是有用的,obj文件夹无用可以删除。libs中的可以看到生成的libdemo.so文件。

两个必要设置

a)、在local.properties中设置NDK路径,我的NDK示例如下:

1
2
sdk.dir=/Users/v1ct0r/Library/Android/sdk
ndk.dir=/Users/v1ct0r/Library/Android/sdk/ndk/21.0.6113669

b)、在app的build.gradle的android节点下设置:

1
2
3
4
5
6
sourceSets
{
main{
jniLibs.srcDirs = ['libs']
}
}

这两处必要的地方该修改完毕后就可以开心的调用我们生成的so文件了。

开始调用

在oncreate后面加入系统加载调用,然后可以直接在oncreate中调用我们的native方法

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
package com.example.love;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.content.Intent;

import android.os.Bundle;

import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView name = findViewById(R.id.editText2);
final TextView pwd = findViewById(R.id.editText);
name.setText(getname());//native函数调用
Button login = findViewById(R.id.button3);
Button register = findViewById(R.id.button);
register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.finish();
}
});
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

String a = String.valueOf(name.getText());
final String b = String.valueOf(pwd.getText());
if(a.equals("yee")&&b.equals("iloveking"))
{
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(MainActivity.this);
alertDialog1.setTitle("Congratulation!!");//标题
alertDialog1.setMessage("You pwn it!");//内容
alertDialog1.setIcon(R.drawable.a);//图标
alertDialog1.setNegativeButton("No", null);
alertDialog1.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 退出当前应用
Intent h = new Intent(MainActivity.this,hhh.class);
h.putExtra("name",b);
startActivity(h);
}
});

alertDialog1.create();
alertDialog1.show();
}
else
{
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(MainActivity.this);
alertDialog1.setTitle("Sorry!!");//标题
alertDialog1.setMessage("账号或者密码错误!");//内容
alertDialog1.setIcon(R.mipmap.ic_launcher);//图标
alertDialog1.create();
alertDialog1.show();
}
}
});
}
public native String getname();//定义我们的方法
static
{
System.loadLibrary("demo");//static中的方法会优先执行,加载我们的.so库
}
}

参考链接:

https://blog.csdn.net/leilifengxingmw/article/details/71598687

这里关于库文件的书写就涉及到我们的ndk开发了,这里就不做深入研究了,具体可以看我自己写的代码:

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
#include <string.h>
#include <jni.h>
jstring Java_com_example_love_MainActivity_getname(JNIEnv *env, jobject thiz)
{
return (*env)->NewStringUTF(env, "I`m Str !");
}

jstring Java_com_example_love_hhh_getpwd(JNIEnv *env, jobject thiz)
{
return (*env)->NewStringUTF(env, "12345678");
}
jboolean Java_com_example_love_MainActivity_kkk(JNIEnv *env, jobject thiz, jstring s)
{
const char *c_str = NULL;
c_str = (*env)->GetStringUTFChars(env, s, 0);
int len = (*env)->GetStringUTFLength(env, s);
char b[100];
char c[100] = "kmomkuw";
for(int i=0;i<len;i++)
{
b[i] = ((c_str[i] + 10)^i);
}
int m = strcmp(b,c);
if(!m)
{
printf("Good!");
(*env)->ReleaseStringUTFChars(env, s, c_str);
return 1;
}
else
{
printf("Wrong!");
(*env)->ReleaseStringUTFChars(env, s, c_str);
return 0;
}
}

这三个函数都是我自己定义的,可以根据需要自己去书写,达到代码保护的作用。

7、fragement的使用

fragement是指片段,可以看成一个子活动,然后内容是可以替换的,有自己的生命周期其实,一般来说有add、remove、replace等操作,这里熟悉下常见的使用方法。

首先在libcs中导入库:android-support-v4.jar

这里面有很多封装函数,比如管理员函数等。

在java文件中的定义,新建一个fragment

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
package com.example.love;

import androidx.lifecycle.ViewModelProviders;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class chatFragment extends Fragment {

public static chatFragment newInstance() {
return new chatFragment();
}

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.chat_fragment, container, false);//fragement界面的回显(当前的fragment,container,false)
}

}

然后在res中的layout文件中自动生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".chatFragment">

<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="聊天界面"
tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

可以在这里设置自己想要的一切布局,textview、button,或者嵌入图片,相当于一个子页面了。然后我们可以设置3个fragment,道理是一样的,接着需要一个管理员去管理这些fragment

管理员界面的生成,一个子活动activity

这里的功能主要是接收点击响应,然后去执行响应的fragemnt跳转,这里使用的是replace进行跳转

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
package com.example.love;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class game1 extends AppCompatActivity {
private FragmentManager fragmentManager;
private FragmentTransaction transaction;
private ImageView image1;
private ImageView image2;
private ImageView image3;

@SuppressLint("ResourceType")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game1);
this.fragmentManager = this.getSupportFragmentManager();//定义管理员
this.transaction = this.fragmentManager.beginTransaction();//获取管理权限
Fragment blank = new BlankFragment();
this.transaction.replace(R.id.abc, blank);//定义想要执行的功能,有add、remove、replace等
this.transaction.commit();//执行定义的功能
this.image1 = this.findViewById(R.id.imageView1);
this.image2 = this.findViewById(R.id.imageView2);
this.image3 = this.findViewById(R.id.imageView3);
}

@SuppressLint("ResourceType")
public void clickTitle(View target) {
LinearLayout layout = (LinearLayout)target;
if (layout == (LinearLayout)this.findViewById(R.id.t1)) {
this.image1.setImageDrawable(this.getResources().getDrawable(R.mipmap.execisesele));
this.image2.setImageDrawable(this.getResources().getDrawable(R.mipmap.course));
this.image3.setImageDrawable(this.getResources().getDrawable(R.mipmap.account));
this.transaction = this.fragmentManager.beginTransaction();//每一次都需要获取下权限
Fragment blankFragment = new BlankFragment();
this.transaction.replace(R.id.abc, blankFragment);
this.transaction.commit();
}
//
if (layout == (LinearLayout)this.findViewById(R.id.t2)) {
this.transaction = this.fragmentManager.beginTransaction();
Fragment testFragment = new TestFragment();
this.transaction.replace(R.id.abc, testFragment);
this.transaction.commit();
this.image1.setImageDrawable(this.getResources().getDrawable(R.mipmap.execise));
this.image2.setImageDrawable(this.getResources().getDrawable(R.mipmap.coursesele));
this.image3.setImageDrawable(this.getResources().getDrawable(R.mipmap.account));
}

if (layout == (LinearLayout)this.findViewById(R.id.t3)) {
this.transaction = this.fragmentManager.beginTransaction();
Fragment chatFragment = new chatFragment();
this.transaction.replace(R.id.abc, chatFragment);
this.transaction.commit();
this.image1.setImageDrawable(this.getResources().getDrawable(R.mipmap.execise));
this.image2.setImageDrawable(this.getResources().getDrawable(R.mipmap.course));
this.image3.setImageDrawable(this.getResources().getDrawable(R.mipmap.accountsele));
}
}
}

同时管理员的xml文件如下:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".game1" >

<FrameLayout
android:id="@+id/abc"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >

</FrameLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal" >

<LinearLayout
android:id="@+id/t1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:onClick="clickTitle"
android:orientation="vertical"
tools:ignore="OnClick">

<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/course" />

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="教程" />

</LinearLayout>

<LinearLayout
android:id="@+id/t2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="clickTitle"
android:orientation="vertical"
tools:ignore="OnClick">

<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/execise" />

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="练习" />

</LinearLayout>

<LinearLayout
android:id="@+id/t3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="clickTitle"
android:orientation="vertical"
tools:ignore="OnClick">

<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/talk" />

<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="对话" />

</LinearLayout>

<LinearLayout
android:id="@+id/t4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="clickTitle"
android:orientation="vertical"
tools:ignore="OnClick">

<ImageView
android:id="@+id/imageView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/community" />

<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="社区" />

</LinearLayout>

<LinearLayout
android:id="@+id/t5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical" >

<ImageView
android:id="@+id/imageView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@mipmap/account" />

<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="账号" />

</LinearLayout>

</LinearLayout>

</LinearLayout>

这里有一个坑点可以自己记一下,就是mipmap中的图片约定是小图,就是那种图标型号的图,但是drawable里面的是大图,二者对图片的解析不同,所以请严格按照自己需求去使用大图和小图,同时大图小图放置的位置也要相应!还有只认png!

8、gridview的使用

叫做网格布局,这是比较规矩的使用,方法如下:

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
package com.example.love;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class hhh extends AppCompatActivity {
int k = 0;
private GridView gridView;
private List<Map<String, Object>> data_list;
private SimpleAdapter adapter;
private int[] icons = {R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher,R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher , R.mipmap.ic_launcher};
private String[] text = {"王者荣耀", "吃鸡", "忍者", "火柴人", "方块", "炮弹战车", "米莱迪", "荣耀", "贪吃蛇", "弹弹堂", "皮卡丘大冒险", "神罗天征", "地包天行", "佐助", "漩涡鸣人","人工智能"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hhh);
Intent intent = getIntent();
TextView text = findViewById(R.id.textView6);
String n = intent.getStringExtra("name");
text.setText(n);
gridView = (GridView) findViewById(R.id.king);//找到定义的网格布局
data_list = new ArrayList<Map<String, Object>>();
getData();//数据初始化
//加载适配器data_list
String[] form = {"image", "text"};
int[] to = {R.id.image, R.id.text};
adapter = new SimpleAdapter(this, data_list, R.layout.item, form, to);//适配器创建
gridView.setAdapter(adapter);//适配器的执行
//监听item每一项
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(hhh.this, "你点击了第" + i, Toast.LENGTH_SHORT).show();
}
});
}

//准备数据源,一个封装的过程
public List<Map<String, Object>> getData() {

for (int i = 0; i < icons.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("image", icons[i]);
map.put("text", text[i]);
data_list.add(map);
}
return data_list;

}
public native String getpwd();
static
{
System.loadLibrary("demo");
}
}

xml文件如下:

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/textView6"
android:layout_width="317dp"
android:layout_height="86dp"
android:layout_marginStart="47dp"
android:layout_marginLeft="47dp"
android:layout_marginEnd="31dp"
android:layout_marginRight="31dp"
android:layout_marginBottom="272dp"
android:gravity="center"
android:text="TextView"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.042" />

<GridView
android:id="@+id/king"
android:layout_width="411dp"
android:layout_height="546dp"
android:columnWidth="90dp"
android:horizontalSpacing="6dp"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
android:verticalSpacing="6dp"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="185dp"
tools:ignore="MissingConstraints"></GridView>

</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

只要部署了gridview即可,因为数据的填入是个动态的过程来的,但是我喜欢用相对布局更多hhhhh。

9、连接数据库

准备工作:mysql数据库、mysql_workbench管理者、mysql-connector-java-5.1.30-bin.jar驱动

首先下载mysql,记住是老版本的!5.1.30左右,新版本有坑,不兼容哎,难受

image-20200512120035542

然后下载个mysql的manager,我用的是mac下的mysql_workbench,主要是用户界面实现操作数据库。

接着在libcs中导入mysql的驱动即可

使用方法:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package com.example.love;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.content.Intent;

import android.os.Bundle;

import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.sql.*;


public class MainActivity extends AppCompatActivity {


public static String getMD5(String str) throws Exception {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
throw new Exception("MD5加密出现错误");
}
}
public void tan(String a,String gg,String ii)
{
if (ii.equals(gg)) {
Looper.prepare();
Log.e("调试", "777");
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(MainActivity.this);
alertDialog1.setTitle("提示:");//标题
alertDialog1.setMessage("登录成功!");//内容
alertDialog1.setIcon(R.drawable.k8);//图标
alertDialog1.create();
alertDialog1.show();
Intent k = new Intent(MainActivity.this, game1.class);
k.putExtra("name", a);
startActivity(k);
Looper.loop();

} else {
Looper.prepare();
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(MainActivity.this);
alertDialog1.setTitle("提示:");//标题
alertDialog1.setMessage("账号或密码错误!");//内容
alertDialog1.setIcon(R.drawable.k8);//图标
alertDialog1.create();
alertDialog1.show();
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 数据库连接操作

final TextView name = findViewById(R.id.editText2);
final TextView pwd = findViewById(R.id.editText);
Button login = findViewById(R.id.button3);
Button register = findViewById(R.id.button);

register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// MainActivity.this.finish();
Intent a = new Intent(MainActivity.this,register.class);
startActivity(a);
}
});
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {//必须用线程的形式去跑,不然起不来,还可以节省空间和时间
public void run() {
try {
// Looper.prepare();
Class.forName("com.mysql.jdbc.Driver");//动态加载类
String url = "jdbc:mysql://192.168.43.166:3306/test";//url定义,网址+端口+数据库名字
Log.e("1", "93994");
Connection conn = DriverManager.getConnection(url, "root", "7758");//输入连接的账号密码
if (conn != null) {
Log.e("调试", "连接成功");
Statement stmt = conn.createStatement();//创建一个执行sql语句的解析器
String a = String.valueOf(name.getText());
String sql = "select * from user where uname=" + "'" + a + "'";
// String sql = "select * from user";
ResultSet rs = stmt.executeQuery(sql); //使用executeQury方法执行sql语句 返回ResultSet对象 即查询的结果
if (rs.next()) { // 遍历结果集ResultSet
//获取这条记录中每列数据,使用ResultSet接口的方法 getXX方法,参数建议写String列名
Log.e("调试", "777");
String gg = rs.getString("upwd");
System.out.println("here--->" + gg);
String d = String.valueOf(pwd.getText());
String ii = getMD5(d);
tan(a,gg, ii);
}
else
{
Looper.prepare();
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(MainActivity.this);
alertDialog1.setTitle("提示:");//标题
alertDialog1.setMessage("该用户不存在!");//内容
alertDialog1.setIcon(R.drawable.i1);//图标
alertDialog1.create();
alertDialog1.show();
Looper.loop();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
});
}
public native String getname();
public native boolean kkk(String a);
static
{
System.loadLibrary("demo");
}
}

千万记得在androidmanifest.xml中加入这句允许远程连接:

1
<uses-permission android:name="android.permission.INTERNET" />

***这里有个坑就是,非ui线程中的弹窗的使用,由于在非ui线程中,所以需要在弹窗的前后加 Looper.prepare()和Looper.loop(),如上的代码可见,相当于开辟一个资源给他用,不会被线程强行遏制住,如果不在非ui线程中,就不用这么操作;ui相关的操作需要在主线程调用,所以如果你是在ui线程中操作,需要转到主线程中,这里可以使用runOnUiThread,其实就是去ui线程更新,具体操作如下:

runOnUiThread() 相当于 是把 ui更新的任务 从 非ui线程 发送到了ui线程,ui线程的handler接受后去处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
final TextView tv_dancishu = view.findViewById(R.id.tv_zhu_book);
new Thread() {
public void run() {
try {
String sql = "select BookName from DanCiShu where flag=1";
ResultSet rs = SQL.search(sql);
if (rs.next()) {
final String bn = rs.getString("BookName");
System.out.println(bn);
getActivity().runOnUiThread(new Runnable() {
public void run() {
tv_dancishu.setText(bn);
}
});
}
} catch (SQLException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}.start();

10、样式的使用

这个简单,就是加入插件的背景,使得插件变得更好看而已,这里在drawable中加入样式文件xml,可以自己定义形状,颜色,长宽比等属性,比如button的样式cc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--<item android:state_pressed="true">-->
<!--<shape android:shape="rectangle">-->
<!--<stroke android:width="1px" android:color="@color/color_light_grey" />-->
<!--<corners android:radius="3dp" />-->
<!--<solid android:color="@color/color_detach_line" />-->
<!--</shape>-->
<!--</item>-->
<item>
<shape android:shape="rectangle">
<stroke android:width="1px" android:color="@color/black" />
<corners android:radius="10dp" />
<solid android:color="@color/white" />
</shape>
</item>
</selector>

样式的使用也很简单,直接在背景中添加想要的样式即可

1
2
3
4
5
6
7
8
9
10
11
12
13
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登陆"
android:textSize="18sp"
android:background="@drawable/cc"//添加样式即可
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.269"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.717" />

11、菜单的生成和使用

首先在res目录下创建menu,然后新建xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item1" android:title="Functions">
<menu>
<item android:id="@+id/menu_item1_item1"
android:title="Map" />
<item android:id="@+id/menu_item1_item2"
android:title="Phone" />
<item android:id="@+id/menu_item1_item3"
android:title="Search" />
</menu>
</item>
<item android:id="@+id/menu_item2" android:title="Setting" />
</menu>

这里就是对于菜单的静态生成

然后在activity中加入我们需要定义的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.game_menu, menu);//获取到menu菜单
return super.onCreateOptionsMenu(menu);
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {//菜单的选择,一个选择对应一个功能
case R.id.menu_item2: // TODO
return true;
case R.id.menu_item1_item1: // TODO
return true;
}
return super.onOptionsItemSelected(item);
}

12、网页请求与回显

1、环境配置:

首先将需要的xUtils-3.3.20.jar包和gson-2.2.2.jar包导入到libc目录下,然后选中右键add as library,就可以使用相应的函数功能了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation files('src/main/libcs/mysql-connector-java-5.1.30-bin.jar')
implementation files('src/main/libcs/gson-2.2.2.jar')
implementation files('src/main/libcs/xUtils-3.3.20.jar')
}

接着在AndroidManifest.xml中设置权限,在application中加入name

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.love">
//设置权限,让其能访问
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:name=".MyApplication" //加自己定义的myapplation类的名字
android:allowBackup="true"
android:icon="@drawable/k8"
android:label="@string/app_name"
android:roundIcon="@drawable/k8"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".test"></activity>
<activity
android:name=".abc"
android:label="@string/title_activity_abc"
android:theme="@style/AppTheme.NoActionBar" />
<activity android:name=".register" />
<activity android:name=".game1" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".hhh" />
</application>

</manifest>

2、创建一个MyApplation.java类,里面进行一个初始化操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.love;


import android.app.Application;
import org.xutils.x;
public class MyApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
//初始化XUtils
x.Ext.init(this);
//设置debug模式
x.Ext.setDebug(false);
}
}

3、创建想要的activity,在里面实现网页请求

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
package com.example.love;
import org.xutils.x;

import org.xutils.http.RequestParams;
import org.xutils.view.annotation.ContentView;
import org.xutils.view.annotation.Event;
import org.xutils.view.annotation.ViewInject;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.xutils.common.Callback;
@ContentView(R.layout.test) //当前窗口
public class test extends AppCompatActivity {

@ViewInject(R.id.textView11)
TextView text; //全局索引到textview11,方便使用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
x.view().inject(this);//初始化完在这里插入
disp();//调用函数
}
@SuppressLint("WrongConstant")
@Event(value = R.id.textView11, type = View.OnClickListener.class)
private void btnClick(View view) {
Toast.makeText(test.this,"test", 1).show(); //toast简易弹窗的使用
}
public void disp() {

String url = "https://www.baidu.com/"; //请求的网站
RequestParams params = new RequestParams(url);//创建request
System.out.println("233333333");
x.http().get(params, new Callback.CommonCallback<String>() {
@Override
public void onCancelled(CancelledException arg0) {
System.out.println("777777777");
}
@Override
public void onError(Throwable arg0, boolean arg1) {
System.out.println("8888888888");
}
@Override
public void onFinished() {
System.out.println("999999999");
}

@Override
public void onSuccess(String arg0) {
Log.e("gson",arg0);
try {
System.out.println("1111111111111");
text.setText(arg0);//回显数据填充
} catch (Exception e) {
System.out.println("2222222222222");
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}

13、打包好项目,生成可安装使用的apk程序文件

1、选择build中的generate signed apk

2、选择apk

3、选择一个key(已经存在的AI.jks)密码是123456

image-20200708083842645

4、选择发行版本并选择第一个jar签名即可

image-20200708083925402

5、然后可以在项目的release目录下找到这个打包签名好的apk文件

0%