0%

Android-note

Android目录结构

  • manifests

    全局描述文件

    • AndroidManifest.xml

  • java

    java源码文件,所有编写的java类都存放于此

    • com.sysz(源码)
    • com.sysz(androidtest) 测试
  • res

    资源文件

    • drawable

      主要用于存放位图文件(PNG\JPG\GIF),9Parch(安卓自带工具生成的矢量图),Shape资源文件

    • layout

      存储安卓应用程序中的布局文件,用于控制窗口显示元素内容

    • mipmap

      保存安卓应用程序的启动图标,即需要适配屏幕分辨率的图片存放地址

    • values

      存放开发过程中需要用的样式、尺寸、字符串等。

Android五大布局管理器

  • RelativeLayout(相对布局管理器)

    相对布局需要存在一个参考点,该参考点可以是其他组件也可以是父容器

    常用属性:

    android:gravity:用于设置布局管理器中各子组件的摆放方式

    android:ignoreGravity:指定组件不受上面gravity影响

    android:layout_above:指定组件位于某个组件的上方

    android:layout_below:指定组件位于某个组件的下方

    android:layout_toLeftOf:指定组件位于某个组件的左侧

    android:layout_toRightOf:指定组件位于某个组件的右侧

    android:layout_alignParentBottom:指定组件相对于父容器底对齐

    android:layout_alignParentLeft:指定组件相对于父容器左对齐

    android:layout_alignParentRigth:指定组件相对于父容器右对齐

    android:layout_alignParentTop:指定组件相对于父容器顶对齐

    android:layout_alignBottom:指定组件与某个组件的底对齐

    android:layout_alignLeft:指定组件与某个组件的左对齐

    android:layout_alignRight:指定组件与某个组件的右对齐

    android:layout_alignTop:指定组件与某个组件的顶对齐

    android:layout_centerHorizontal:指定组件位于布局管理器水平居中

    android:layout_centerInParent:指定组件位于布局管理器中间位置

    android:layout_centerInVertical:指定组件位于布局管理器垂直居中

  • LinearLayout(线性布局管理器)

    将布局中的子组件以垂直或者水平方向排列,线性布局管理器中超出屏幕宽度的组件将不会显示

    常用属性:

    android:orientation=”vertical/horizontal” 指定线性布局管理器为垂直/水平排列

    android:gravity:用于设置布局管理器中各子组件的摆放方式

    android:layout_weight:设置子组件所占空间的权重,

  • FrameLayout(帧布局管理器)

    该布局中的组件会按照先后顺序层叠放置,后面的组件会覆盖前面的组件

    常用属性:

    android:foreground:为当前的布局管理器设置前景图(始终位于最上层的图)

    android:foregroundGravity:设置前景图位于布局容器中的位置

  • TableLyout(表格布局管理器)

    该布局管理器将会以行列的方式管理其中的组件,该布局管理器只支持跨行不支持跨列!

    没有列标记只存在\行标记,直接添加组件默认为添加一行。

    android:stretchColumns=允许某一列被拉伸

    android:shrinkColumns=允许某一列被压缩

  • GridLayout(网格布局管理器)

    Android4.0版本后推出,将页面划分为行列,允许跨行和跨列,这使得它比表格布局管理器更加灵活

    常用属性:

    android:columnCount=指定网格最大列数

    android:orientation=指定组件在没有规定行列时的排列方式

    android:rowCount=指定网格最大行数

    android:layout_column=指定子组件位于网格的第几列

    android:layout_columnSpan=指定子组件横向跨列数

    android:layout_rowSpan=子组件纵向跨行数

    android:layout_columnWeight=子组件在水平方向的权重

    android:layout_rowWeight=子组件在垂直方向的权重

    android:layout_gravit=子组件以什么方式占据空间

    android:layout_row=指定子组件位于网格的第几行

Activity对象

  • Activity类似于Vue.js开发中的每一个页面VM实例,具有自己的生命周期

  • Activity分为两种:

    • 入口Activity

      需要在AndroidManifest.xml中进行配置

    • 其他Activity

      1.通过startActivity来启动第二个Activity

      2.通过this.finish();关闭Activity

      1
      2
      3
      //new Intent(上下文对象,需要启动的Activity)   
      Intent intent = new Intent(context, Home.class);
      startActivity(intent);

      3.onCreate(null)刷新当前Activity

  • Activity交换传递数据

    Aciivity通过Intent的putExtras()方法装载Bundle对象传递数据,Bundle对象是以key-value的方式保存数据

    1
    2
    3
    4
    5
    6
    //        获取上一Activity传递的数据
    Intent intent = getIntent();
    Bundle extras = intent.getExtras();
    account = extras.getString("account");
    pwd = extras.getString("pwd");
    this.setValue(account, pwd);
  • Activity调用另一个Activity并返回数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //调用者Activity
    Intent intent = new Intent(MainActivity.this, Home.class);
    startActivityForResult(intent,0x11);
    //重写onActivityResult来获取返回的数据
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    // 判断请求码和响应码是否相同
    if (requestCode == 0x11 && requestCode == 0x11) {
    Bundle build = data.getExtras();
    int imageId = build.getInt("imageId");
    }
    }
    //被调用者Activity
    Intent intent = getIntent();
    Bundle bundle = new Bundle();
    bundle.putInt("key",value);
    intent.putExtras(bundle);
    setResult(0x11,intent);
    finish();

Android Adapter(适配器)

适配器主要用于链接后端数据和前端显示的接口,是数据和UI组件的重要桥梁.

适配器

常用适配器

  • ArrayAdapter(数组适配器)

    通常用于将数组中的值包装成多个列表项,他只能显示一行文字,

  • SmipleAdapter(简单适配器)

    通常用于将List集合的项包装成多个列表项,可以自定义各种效果,功能十分丰富

  • SmipleCursorAdapter

    主要用于一般主要用于数据库,它的数据来源一般都是数据库查询得到的Cursor

    SimpleAdapter的参数说明
    第一个参数 表示访问整个android应用程序接口,基本上所有的组件都需要
    第二个参数表示生成一个Map(String ,Object)列表选项
    第三个参数表示界面布局的id 表示该文件作为列表项的组件
    第四个参数表示该Map对象的哪些key对应value来生成列表项
    第五个参数表示来填充的组件 Map对象key对应的资源一依次填充组件 顺序有对应关系

  • BaseAdapter

    可以最大限度的定制每一个列表项的布局样式,灵活性高,

    实例:

    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.iflytek.adapter;

    import java.util.ArrayList;
    import java.util.List;

    import com.iflytek.iflytekbus.R;
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.TextView;

    public class NewBaseAdapter extends BaseAdapter{

    private Context context;
    private ArrayList<String> list;
    private int resource;
    private LayoutInflater inflater;
    public NewBaseAdapter(Context context,List<String> items ,int resource) {
    // TODO Auto-generated constructor stub
    this.context=context;
    this.inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.list=(ArrayList<String>) items;
    this.resource=resource;
    }
    private class ViewHolder{
    private TextView txtView;
    }
    @Override
    public int getCount() {//返回list的所有数量
    // TODO Auto-generated method stub
    return list.size();
    }

    @Override
    public Object getItem(int arg0) {//当前的一个对象
    // TODO Auto-generated method stub
    return list.get(arg0);
    }

    @Override
    public long getItemId(int arg0) {//当前对象的一个索引
    // TODO Auto-generated method stub
    return arg0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    ViewHolder holder=new ViewHolder();
    if(convertView==null){
    String string=list.get(position).toString();
    convertView=inflater.inflate(resource, null);
    holder.txtView=(TextView)convertView.findViewById(R.id.myListData);
    holder.txtView.setText(string);
    }
    else {
    holder=(ViewHolder)convertView.getTag();
    }
    return convertView;
    }

    }

Fragment

意为碎片。Fragment是依赖于Activity的,不能独立存在的

Fragment的生命周期和Activity类似,但比Activity的生命周期复杂一些

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用。
  • onCreateView():创建Fragment的布局。
  • onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

Fragment与Activity通讯

1.Fragment中定义接口

2.Fragment定义设置接口的方法

3.Activity实现接口

4.Fragment添加监听,事件处理环节调用事件处理接口,具体操作由Activity进行

5.Activity的onAttachFragment方法中,添加Fragment设置接口的方法将自身引用传入

静态调用

在配置文件(xml)中描述fragment的装载关系,将Frament作为占位标记

1
2
3
4
5
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="com.example.login_text.fragment.LoginFragment"
android:id="@+id/frag_01"/>

动态调用

在控制文件(java)中描述fragment的装载关系,更加灵活,实际开发中也是比较常用的方法

Intent对象

在安卓开发过程中如果我们把Activity比作是一个人,那么这个人就可通过Intent来表达自己的想法/意图

  • 在Activity运行时App能够根据屏幕的状态自动添加、删除、替换组成用户界面的Fragment或Framengt中的组件,能够自动重新组织Fragment的布局,为用户提供一个自适应的操作界面,
  • Android系统使用FragmentMannager创建FragmentTrasaction来完成Fragment事务处理

Android屏幕适配

  • 屏幕尺寸、分辨率、像素密码三者关系

    一部手机的分辨率是宽x高,屏幕大小是以寸为单位

  • dpi

  • 方式一:

    通过supports-screens进行适配

    1
    2
    3
    4
    5
     <supports-screens
    android:compatibleWidthLimitDp="320"
    android:largestWidthLimitDp="1080"
    android:smallScreens="false" />
    //当屏幕最小边宽度小于320和大于1080时时则不适应,使用兼容模式运行
  • 方式二:

    不同屏幕做不同布局

  • 方式三:

    不同屏幕做不同的位图,

    备用位图放在drawble目录下的不同对应密度目录中,系统会根据屏幕dpi选择适应的位图

    应用图标放在mipmap目录中,所有mipmap目录都会保留在APK中

  • 点9图

Android资源目录

Android菜单栏

  • 弹出菜单(关键代码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //        创建弹出菜单 参数二即为需要显示的父控件位置
    PopupMenu popupMenu = new PopupMenu(this, View);
    // 将item填充的弹出菜单
    popupMenu.inflate(R.menu.menu_main);
    // 绑定事件监听
    popupMenu.setOnMenuItemClickListener(this);
    popupMenu.show();

    @Override
    public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.xxxx:
    // 事件处理
    case ;
    }
    return false;
    }
  • 选项菜单

    即顶部ActionBar,最右端的三个点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
    return true;
    }

    return super.onOptionsItemSelected(item);
    }
  • 上下文菜单

    当用户长按某个视图或视图元素后出现的浮动菜单,菜单中包含的动作是与用户所选择视图元素相关的

    Android系统定义了两种模式的上下文菜单

    浮动模式:

    创建步骤:

    1.创建菜单配置文件xml

    2.添加弹出菜单到对应的view控件上

    • 覆盖Activity的onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法
    • 调用Menu的add()方法添加菜单项(MeunItem)
    • 调用registerForContextMenu(imageView);方法为视图注册上下文菜单

    3.定义菜单事件监听

    • 覆盖Actiyity的onContextItemSelected(@NonNull MenuItem item)

    动作模式

    通过ActionMode的系统实现,可以在屏幕顶部显示上下文动作条,其中的菜单项是与所选视图元素相关的动作,用户在使用上下文菜单的动作条中选择一个或多个动作。

    这种模式只有在Android3.0或者更高版本可用,是使用上下文菜单的推荐模式

    创建步骤

    创建弹出菜单配置文件xml

    添加弹出菜单对应view控件上

    • 创建ActionMode

      private android.view.ActionMode actionMode

    • 实现ActionMode的callback接口

    • 调用setOnlongClickListener()方法视图注册上下文菜单

    定义菜单事件监听

    • 设置控件的OnLongClickListener()来响应事件
  • 菜单组

    使用可以对菜单项进行分组

    • 菜单组可以在菜单资源文件中定义,把元素嵌套进元素中来创建分组菜单
    • 在Android应用程序中,使用带有分组ID的add()方法创建分组
    • 对于同一个中的,可以通过menu执行以下操作:
      • ppublic void setGroupVosoble(int group,boolean visible)
        • 显示或隐藏组内的所有项目
        • group指的是元素的id属性
      • public void setGroupEnabled(int group,boolean enabled);
        • 启用或禁用组内的所有项目
      • public void setGroupCheckable(int group,boolean checkable,boolean exclusive)
        • 指定组内的所有项目是否可选
        • exclusive为true,代表菜单项为单选模式,否则为多选模式

菜单启动外部应用

Android广播

  • 广播Broadcast是一种广泛运用在应用程序之间用于传送消息的机制。
  • 多数广播是系统发起,如地域变换、电量不足、来电来信等。
  • 应用程序自身也可以发出一个广播

Broadcast Receiver组件(广播接收者)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.forvue.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.example.forvue.MainActivity;

public class MyReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String info = intent.getStringExtra("msg");
Log.i("广播内容", info);
// MainActivity.info.setText(info);

// Log.i(intent.getAction(),intent.getStringExtra("msg") );
// Toast.makeText(context, "收到广播", Toast.LENGTH_SHORT).show();
}
}

Broadcast Receiver时一段独立的java程序代码,需要在Android项目中注册后才能使用(类似Activity)

注册方法:

静态注册:

Android8.0(API级别26)以下版本,在AndroidManifest.xml文件中使用注册要使用Broadcast Receiver,并在的子元素中定义过滤条件,确定接受哪一类intent

1
2
3
4
5
6
7
8
9
10
>><!--定义静态接收器-->
<!--Enabled属性的作用是是否启动这个广播接受器Exported属性的作用是是否允许这个广播接收器接受本程序以外的广播-->
<receiver
android:name=".receiver.MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.liyi.BROADCAST" />
</intent-filter>
</receiver>

发送广播:

1
2
3
4
5
6
>>//,指的是要发送一条广播,并且指定了广播的名称,这个跟我们之前注册的广播名称一一对应。
>>Intent intent = new Intent("com.liyi.BROADCAST");
>>intent.setComponent(new
//在Android 7.0及以下版本不是必须的,但是Android 8.0或者更高版本,发送广播的条件更加严苛,必须添加这一行内容第1个参数是指接收广播类的包名,第2个参数是指接收广播类的完整类名。 ComponentName("com.example.forvue","com.example.forvue.receiver.MyReceiver"));
>>intent.putExtra("msg", "这是一条广播信息");
>>sendOrderedBroadcast(intent,null);

静态注册的应用程序会一直进行监听(无论是否处于活跃状态)

动态注册:

在java中通过Content.registerReceiver()方法来注册

动态实现注册类必须时BroadcastReceiver的子类

动态注册:

1.定义广播

2.定义接收器

3.动态注册接收器

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
final MyReceiver[] receivers = {null};   
>>@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_01:
>>// 定义广播
Intent intent = new Intent("sendMsg");
intent.putExtra("msg", "这是一条最新的广播");
sendBroadcast(intent);
break;
case R.id.btn_02:
receivers[0] = new MyReceiver();
>>// 创建过滤器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("sendMsg");
>>// 注册接收器
registerReceiver(receivers[0], intentFilter);
System.out.println("注册");
break;
case R.id.btn_03:
>>// 取消接收器
unregisterReceiver(receivers[0]);
break;
}

}

TIP:

1、动态优先级大于静态

2、静态程序关没关都能收到信息,而动态应用程序一关就不能接收到!

  • 广播发出以后,由组件BroadcastReceiver过滤接受并响应。它可以监听系统全局的广播信息,实现系统中不同组件之间的通讯
  • BroadcastReceiver类是所有广播接收器的基类
  • 消息到达时,BroadcastReceiver调用onReceive()方法
  • 在该方法结束后,BroadcastReceiver实例失效

发送广播

  • Android系统提供了两种方法发送广播
    • Context.sendBroadcast()发送广播,所有满足条件的BroadcastReceiver都会执行其onReceive()方法来处理响应
    • Context.sendOrdderedBroadcast()发送的有序广播。会根据BroadcastReceiver注册时IntentFilter的优先级顺序来执行onReceive()方法。
      • 优先级在的android:priority中声明,也可以在代码中通过intentFilter.setPrioriy()方法设置,数越大优先级越高。

WebView

android调用vue

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
125
126
package com.example.forvue;

import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.TextView;

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


import com.example.forvue.receiver.MyReceiver;

import butterknife.BindView;
import butterknife.ButterKnife;


public class MainActivity extends AppCompatActivity {
public static TextView info;
final MyReceiver[] receivers = {null};
@BindView(R.id.myWebView)
WebView webView;
int REQUEST_CODE_SCAN = 100;
@BindView(R.id.btn_01)
Button button1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//将当前对象绑定到组件ButterKnife中
ButterKnife.bind(this);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
WebSettings ws = webView.getSettings();
ws.setBlockNetworkImage(false);
ws.setJavaScriptEnabled(true);
ws.setLoadWithOverviewMode(true);
ws.setUseWideViewPort(true);
ws.setDefaultTextEncodingName("utf-8");
ws.setLoadsImagesAutomatically(true);
ws.setSupportZoom(false);
ws.setBuiltInZoomControls(false);
ws.setDomStorageEnabled(true);
ws.setAppCacheEnabled(true);
ws.setAllowFileAccess(true);
ws.setJavaScriptCanOpenWindowsAutomatically(true);
ws.setCacheMode(WebSettings.LOAD_NO_CACHE);
//重定向时调用
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
//vue项目所在地址
webView.loadUrl("http://192.168.0.155:8080/");
//设置 vue 需要调用的安卓方法,及调用对象
webView.addJavascriptInterface(new AndroidToJs(),"android");
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
webView.post(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void run() {
webView.evaluateJavascript("javascript:callJs("+123+")", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.i("vue", value);
}
});
}
});
}
});

// 对alert进行重构
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}

});

}


class AndroidToJs extends Object {
// 定义JS需要调用的方法
// 被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
System.out.println(msg);
}
}

}

vue:

1
2
3
4
5
6
7
8
9
10
11
12
mounted() {
//将vue被android调用的方法挂在到window对象上
window.callJs = this.callJs;
}
methods:{
callJs(val){
alert(val)
},
callAndroid() {
android.hello("vue传递的数据");
}
}

成功截图:

forAndroid

forAndroid

forVue

Android网络请求

  • 使用java原生的网络请求API

    MainAcitvity

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

    import androidx.appcompat.app.AppCompatActivity;

    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;

    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;

    public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    }

    public void loadJson(View view) {
    // 实际开发中不要乱制造线程,必须规范创建线程并且将每一个线程管理并监控
    new Thread(new Runnable() {
    @Override
    public void run() {
    // 发送http网络请求
    try {
    URL url = new URL("http://192.168.0.155:9102/get/text");
    // 创建一个链接
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    // 设置响应超时时间
    connection.setConnectTimeout(10000);
    // 设置请求方式
    connection.setRequestMethod("GET");
    // 设置请求格式
    connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"); //响应内容的语言
    // connection.setRequestProperty("Accept-Encoding", "gzip, deflate, br");//响应内容的编码格式
    connection.setRequestProperty("Accept", "*/*"); //响应内容格式
    connection.connect(); //发送
    //结果码
    int responseCode = connection.getResponseCode();
    if (responseCode == 200) {
    System.out.println("请求成功");
    Map<String, List<String>> headerFields = connection.getHeaderFields();
    Set<Map.Entry<String, List<String>>> entries = headerFields.entrySet();
    for (Map.Entry<String, List<String>> entry : entries) {
    Log.i(TAG, entry.getKey() + "=====" + entry.getValue());
    }
    InputStream inputStream = connection.getInputStream();
    //
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line = bufferedReader.readLine();
    Log.i(TAG, line);

    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }).start();

    }
    }

    tip:Android API 27以下版本是不能采用http请求方式,需要在Manifest文件中配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    android:networkSecurityConfig="@xml/network_sercurity_config"

    network_sercurity_config.xml
    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
    <domain-config>
    <domain includeSubdomains="true">example.com</domain>
    <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">secure.example.com</domain>
    </domain-config>
    </domain-config>
    </network-security-config>

数据存储

数据存储方式

Android应用程序可以选择的数据存储方式包括以下几种

  • Shared Preferences

    用键值对的形式保存私有的原始数据

  • Internal Storage

    在设备的内部存储上保存私有的数据,这是内置在设备中不可以任意移除的存储

  • External Storage

    在共享的外部存储器上保存公共数据,这是扩充的存储,可以任意移除

  • SQLite数据库

    在私有的数据库中保存结构化的数据

  • NetWork Connection

    把数据保存在网络互联网服务器上,例如云存储

SharedPreferences

  • android提供了一种Shared Preferences的存储方式,可以称其为共享偏好的存储方式

  • 顾名思义,用于保存一些常用的配置:如窗口状态、用户参数

  • Shared Preferences是一种轻量级的句子,通过key-value保存数据,其存储格式为XML格式

    • 路劲: data/data/应用的程序包名/share_prefs/文件名.xml
  • Shared Preferences处理数据有三种模式

    • MODE_PRIVATE(默认):只有创建该Preferences的程序才能访问
    • MODE_WORLD_READABLE:全局可用,但是其他程序只有只读权限
    • MODE_WORLD_WRITEABLE:全局可用,并且同时拥有读写权限
  • 在程序中获取Shared Preferences对象有两种方法

    • Context.getSharedPreferences(String name,int mode)
      • name:本组件的配置文件名
      • int:操作模式,默认是0或MODE_PRIVATE
    • Activity.getPreferences(int mode)
  • 代码示范

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      //        创建偏好对象
      SharedPreferences sp = getSharedPreferences("token", Context.MODE_PRIVATE);
      // 获取编辑器
      SharedPreferences.Editor edit = sp.edit();
      // 存储数据
      edit.putString("", "");
      edit.putInt("",)
      edit.putBoolean("",)
      // 提交更改后的配置文件
      edit.commit();
    • 1
      2
      3
      4
      5
      //        根据存储的key获取之前存储的数据
      // 参数一:key 参数二:默认值
      sp.getString("", "");
      SP.getInt("",)
      sp.getBoolean("",)

理解Preferences

  • 在应用中,常常需要存储用户的个人设置(偏好),针对这一应用场景,Google创建了一个偏好框架,提供一种机制使开发者能够很容易地显示,保存和操纵用户的偏好

  • 将偏好以键值对的形式保存在应用程序默认的SharedPreferences文件中。当用户改变设置时,系统会跟新Shared Preferences 文件中对应的值。

    读取Shared Preferences 文件中的数据,可以根据用户的共享参数改变应用程序行为。

  • 设计步骤

    • 定义布局

    • 加载布局

      • extends PreferenceActivity
      • addPreferencesFromResource(R.xml.preferences)
    • 定义事件

      • implements OnSharedPreferenceChangeListener

Android地图

定位服务

  • Android通过andorid.location包中的类为应用程序提供定位服务

  • 定位框架中的核心组件就是LocationManager系统服务,其提供了支撑底层设备的定位API

  • 与其他系统服务一样,Android通过调用Context.getSystemService(Context.LOCATION_SERVICE)获得一个LocationManager对象。

  • 使用Google地图的Android API,可以使用Google地图数据,将地图功能集成到应用中,API自动处理对Google地图服务器的访问、数据下载、地图显示和地图上触控手势

  • Android设备获取位置可以使用GPS和Android网络位置提供器(Android Network Location Provider,NLP)

  • 获取位置信息时要解决的问题

    • 多种位置源

      常用信息源:

      1.GPS 定位,精度高,但只适用于护外

      2.基站定位:通过三个基站交叉定位 三点定位 ,但基站的分布不是平均的

      3.网络定位,通过IP地址精准定位,但是需要流量并且网络需要稳定

    • 用户移动

    • 变化精度

  • 实现定位功能的重要类

    • LocationManager

      该类提供访问系统定位服务

    • LocationProvider

      它是一个抽象类,是不同定位提供的父类,提供当前位置信息,并存储Location类中。

    • LocationListener

      该类提供定位信息发生改变时的回调功能。必须事先在定位管理器中注册监听器对象

    • Criteria

      该类使得应用能通过在LocationProvider中设置的属性来选择合适的定位提供者。

    • 请求位置更新信息

      在Android中,可以通过回调的方法得到用户位置。使用LocationMananger类,向其requestLocationUpdates()方法传入一个LocationListener对象,就可以获得位置更新

    • 用户权限设置

      为了从NETWORK_PROVIDER或GPS_PROVIDER获取位置更新,必须在应用程序的Manifest文件中声明用户访问的ACESS_COARSE_LOCATION或ACCESS_FINE_LOCATION权限。

  • 获取用户位置的典型流程:

    • 启动应用
    • 一段时间后,开始监听定位提供者获取位置信息
    • 通过取出不够准确的位置更新来保持以最佳状态去获取位置信息。
    • 停止监听获取位置信息
    • 采用最新最好的位置

关键点策略

  • 在接受更新位置信息的这段时间,需要对以下关键点做出决策

    1. 决定开始监听更新的时刻
    2. 通过最后可知位置快速修正
    3. 决定停止监听更新的时刻
    4. 保持最佳的估算值
    5. 调整模型来保存电量和数据交换
    6. 减少窗口大小
    7. 减少位置提供者的更新频率
    8. 仅支持一种位置信息提供者
  • 确认位置修正可以采用的步骤

    • 检查是否最近得到的位置信息明显比以前的要新
    • 检查位置京都是好于还是差于之前的位置信息
    • 检查最新的位置信息来自哪一个提供者,并且判断这个位置信息相比之前的是否是更加准确可靠

调试位置数据

  • 向应用提供模拟数据的方法主要有:Eclipse,DDMS或者模拟器控制台geo命令行
  • 由于提供者模拟位置数据使用的是GPS的数据类型,所以必须使用GPS_PROVIDER来获取位置更新,否则模拟数据无法工作。

实现位置信息获取

  • 具体步骤:
    1. 设置用户权限
    2. 定义布局等资源文件
    3. 创建或打开Activity,获取视图对象
    4. 获取LocationManager对象
    5. 定义自己的LocationListener
    6. 将MyLocationLister注册到当前的LocationManager对象
    7. 定义按钮的监听器

百度地图

1.在百度开发者平台注册自己的应用程序

控制台 | 百度地图开放平台 (baidu.com)

百度开放平台

应用管理——>我的应用——>创建应用

创建应用程序

![AK]AndoridAK.png)

选择AK 点击复制在我们本地应用程序的Mainfest文件将会用到

Android定位SDK | 百度地图API SDK (baidu.com)

定制包

定制包类型

再对应的sdk文档中点击产品下载下载Android sdk,并根据自己的实际需要定制sdk包

选择你想要解压的目录,打开之后可以看到上图所示的目录,根据你操作系统的类型选择对应的目录复制粘贴的Android应用程序的libs文件夹

2.本地Andorid配置

  • 在build.gradle中 引入我们本地libs库

    1
    2
    3
    4
    5
    sourceSets{
    main{
    jniLibs.srcDirs=['libs']
    }
    }
  • Manifest.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
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.ygst">
    <!--网络访问权限-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 网络定位权限-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!--GPS定位权限-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!--访问WIFI网络信息 WIFI信息用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!--获取运行商信息,用于支持提供运行商信息的接口-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!--WIFI获取权限,WIFI信息将会用于网络定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <!--写入扩展存储,向扩张卡写入数据,用于写入离线定位数据-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--android9.0上使用的前台服务,需要添加权限-->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <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">
    <meta-data
    android:name="com.baidu.lbsapi.API_KEY"
    android:value="jR9jnYC4MiBg7s4hkdi9qZI6DPp7TTUq" />

    <activity android:name=".views.BDMapActivity">

    </activity>
    <activity android:name=".MainActivity">
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>
    <!--引入百度地图service-->
    <service
    android:name="com.baidu.location.f"
    android:enabled="true"
    android:process=":remote" />
    </application>

    </manifest>
  • Activity.java

    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
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    package com.example.ygst.views;

    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.app.ActivityCompat;
    import androidx.core.content.ContextCompat;

    import android.Manifest;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.widget.TextView;
    import android.widget.Toast;

    import com.baidu.location.BDAbstractLocationListener;
    import com.baidu.location.BDLocation;
    import com.baidu.location.LocationClient;
    import com.baidu.location.LocationClientOption;
    import com.baidu.mapapi.SDKInitializer;
    import com.baidu.mapapi.map.BaiduMap;
    import com.baidu.mapapi.map.MapStatusUpdate;
    import com.baidu.mapapi.map.MapStatusUpdateFactory;
    import com.baidu.mapapi.map.MapView;
    import com.baidu.mapapi.map.MyLocationData;
    import com.baidu.mapapi.model.LatLng;
    import com.example.ygst.MainActivity;
    import com.example.ygst.R;

    import java.util.ArrayList;

    public class BDMapActivity extends AppCompatActivity {

    LocationClient mLocationClient;
    // 第一次获取位置 显示详细信息
    boolean isFistLocation = true;
    private TextView locationInfo;
    private MapView mMapView;
    private BaiduMap mBaidMap = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 初始化百度sdk
    SDKInitializer.initialize(getApplicationContext());
    setContentView(R.layout.activity_b_d_map);
    locationInfo = this.findViewById(R.id.location_info);
    mLocationClient = new LocationClient(getApplicationContext());
    //初始化监听器
    mLocationClient.registerLocationListener(new MyLocationListener());
    //发送请求定位信息
    //初始化地图
    mMapView = this.findViewById(R.id.bd_map);
    mBaidMap = mMapView.getMap();
    // 地图类型 微信视图
    mBaidMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
    // 开启定位
    mBaidMap.setMyLocationEnabled(true);

    // requestLocation();
    //安卓6.0以后需要动态申请权限
    ArrayList<Object> permissionList = new ArrayList<>();
    if (ContextCompat.checkSelfPermission(BDMapActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

    permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
    }
    if (ContextCompat.checkSelfPermission(BDMapActivity.this, Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED) {

    permissionList.add(Manifest.permission.ACCESS_WIFI_STATE);
    }
    if (ContextCompat.checkSelfPermission(BDMapActivity.this, Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED) {

    permissionList.add(Manifest.permission.ACCESS_NETWORK_STATE);
    }
    if (ContextCompat.checkSelfPermission(BDMapActivity.this, Manifest.permission.CHANGE_WIFI_STATE) != PackageManager.PERMISSION_GRANTED) {

    permissionList.add(Manifest.permission.CHANGE_WIFI_STATE);
    }

    if (!permissionList.isEmpty()) { //如果没有获取到权限
    String[] permissions = permissionList.toArray(new String[permissionList.size()]);
    ActivityCompat.requestPermissions(BDMapActivity.this, permissions, 1);
    } else {
    requestLocation();
    }
    }

    // 如果用户没有赋予权限则执行回到函数处理
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode) {
    case 1:
    if (grantResults.length > 0) {
    for (int result : grantResults) {
    //如果被拒绝
    if (result != PackageManager.PERMISSION_GRANTED) {
    Toast.makeText(this, "必须同意所有的权限才能正常使用APP", Toast.LENGTH_LONG).show();
    finish();
    return;
    }
    }
    requestLocation();
    } else {
    Toast.makeText(this, "出现未知错误", Toast.LENGTH_LONG).show();
    }
    break;

    }
    }

    private void requestLocation() {
    // 发送前先初始化定位参数
    initLocation();
    // 开始监听
    mLocationClient.start();

    }

    private void initLocation() {
    LocationClientOption option = new LocationClientOption();
    /**
    * 可选,设置定位模式,默认高精度
    * LocationMode.Hight_Accuracy;高精度
    * LocationMode.Battery_Saving;低功耗
    * LocationMode.Device_Sensors; 仅使用设备
    */
    option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
    /**
    * 可选。设置返回近卫笃坐标类型,默认 GCJ02
    * GCJ02:国测局坐标
    * BD09LL:百度经纬度坐标
    * BD09:报读墨卡托坐标
    * 海外地区定位,无需设置坐标类型,统一返回WGS84类型坐标
    */
    option.setCoorType("bd09ll");
    /**
    * 可选,设置发起定位的时间间隔,int类型,单位ms
    * 如果设置为0,则代表单次定位,即只定位一次,默认0
    * 如果设置非0,需要设置1000ms以上才有效
    */
    option.setScanSpan(1000);
    /**
    * 可选,设置是否使用gps,默认false
    * 使用高精度和仅用设备两种定位模式的,参数设置为true
    */
    option.setOpenGps(true);
    /**
    * 可选,定位SDK内部是一个service,并放到了独立进程
    * 设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)
    *
    */
    option.setIgnoreKillProcess(false);
    /**
    * 可选,设置是否当GPS有效时按照1s/1次频率输出GPS结果,默认false
    */
    option.setLocationNotify(false);
    /**
    * 可选,V7.2版本新增能力
    * 如果设置了该接口,首次启动定位时,会先判断当前Wi-Fi是否超出有效期,若超出有效期
    * 会先重新扫描Wifi,然后定位
    */
    option.setWifiCacheTimeOut(5 * 60 * 1000);
    /**
    * 可选,设置是否要过滤GPS仿真结果,默认需要,即参数为false
    */
    option.setEnableSimulateGps(false);
    option.setIsNeedAddress(true);
    // 应用
    mLocationClient.setLocOption(option);
    }

    private void navigateTo(BDLocation bdLocation) {
    if (isFistLocation) {
    //经纬度
    LatLng latLng = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());
    // 创建地图更新 将获取到的经纬度放入
    MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng);
    // 更新地图
    mBaidMap.animateMapStatus(update);
    //放大
    update = MapStatusUpdateFactory.zoomTo(16f);
    mBaidMap.animateMapStatus(update);
    isFistLocation = false;
    }
    //当前定位的构建者
    MyLocationData.Builder builder = new MyLocationData.Builder();
    builder.longitude(bdLocation.getLongitude());
    builder.latitude(bdLocation.getLatitude());
    //构建位置信息
    MyLocationData locationData = builder.build();
    //标明信息出来
    mBaidMap.setMyLocationData(locationData);
    }

    @Override
    protected void onDestroy() {
    super.onDestroy();
    //销毁时回收资源
    mLocationClient.stop();
    mMapView.onDestroy();
    mBaidMap.setMyLocationEnabled(false);
    }

    @Override
    protected void onResume() { //暂停时
    super.onResume();
    mMapView.onResume();
    }

    @Override
    protected void onPause() {
    super.onPause();
    mMapView.onPause();
    }

    private class MyLocationListener extends BDAbstractLocationListener {

    @Override
    public void onReceiveLocation(BDLocation bdLocation) { //收到定位信息时
    //定位到当前GPS信息
    navigateTo(bdLocation);

    // StringBuffer sb = new StringBuffer();
    // sb.append("纬度:").append(bdLocation.getLatitude()).append("\n");
    // sb.append("经度:").append(bdLocation.getLongitude()).append("\n");
    // sb.append("国家:").append(bdLocation.getCountry()).append("\n");
    // sb.append("省:").append(bdLocation.getProvince()).append("\n");
    // sb.append("市:").append(bdLocation.getCity()).append("\n");
    // sb.append("区:").append(bdLocation.getDirection()).append("\n");
    // sb.append("村镇:").append(bdLocation.getTown()).append("\n");
    // sb.append("街道:").append(bdLocation.getStreet()).append("\n");
    // sb.append("地址:").append(bdLocation.getAddrStr()).append("\n");
    // sb.append("定位方式:");
    // if (bdLocation.getLocType() == BDLocation.TypeGpsLocation) {
    // sb.append("GPS");
    // } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {
    // sb.append("网络");
    // }
    // locationInfo.setText(sb.toString());

    }
    }
    }
  • layout.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
    <?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".views.BDMapActivity">

    <TextView
    android:id="@+id/location_info"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone" />

    <com.baidu.mapapi.map.MapView
    android:id="@+id/bd_map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true">

    </com.baidu.mapapi.map.MapView>
    </LinearLayout>

tip:本文只是百度Map定位服务的一个最简单的应用,详细使用请阅读官方API参考文档

Andorid四大组件之一Service(服务)

什么是服务?

简单的说就是Andorid长期运行在后台的程序,官方说,首先它是一个组件,其次它执行长期任务,最后它和用户并没有交互。

为什么要使用服务?

边听歌边看微博,这个应用场景就诠释了我们为什么需要使用服务。

进程类型:

1、前台进程:可以理解为是最顶部的,直接跟用户交互的。比如说我们操作的Activity界面.

2、可见进程:可以见的,但是不操作的,比如说我们在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity则是可见进程。

3、服务进程:服务可以理解为是忙碌的后台进程,虽然是在后台,但是它很忙碌。

4、后台进程:后台进程就是退隐到后台,不做事的进程。

5、空进程:空进程是不做事的,没有任何东西在上面跑着,仅作缓存作用。

假设,内存不够用了,会先杀谁呢?

首先杀的是空进程,要是还不够就杀后台进程,要是还不够,那么就杀服务,但是服务被杀死以后,等内存够用了,服务又会跑起来了。

阳光沙滩-学院 (sunofbeach.net)

服务的生命周期:

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
package com.liyi.servicedemo.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.Nullable;

import com.liyi.servicedemo.interaces.ICommuncation;

public class FirstService extends Service {
private static final String TAG = "FirstService";

@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind.....");
return new InnerBind();
}

@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate.....");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand.....");
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy.....");
}

private void sayHello() {
Toast.makeText(this, "Hello World", Toast.LENGTH_SHORT).show();
}

private class InnerBind extends Binder implements ICommuncation {
public void callServiceInnerMethod() {
sayHello();
}

}
}

Android循环线程

1
2
3
4
5
6
7
8
9
10
final Handler handler=new Handler();  
final Runnable runnable=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//要做的事情
handler.postDelayed(this, 1000); // 1秒后执行
}
};
handler.postDelayed(runnable, 1000);//1秒执行一次runnable.

Andorid mvp目录结构

  • activtiy

    视图层 (view),一般用于初始化视图以及view的实现

  • contract

    契约,主要作为一个纽带,将试图层(view)和model(数据层)链接并最终注入到presenter层

  • module

    Module

    1
     

    类上必须加上@Module注解,Module是模块的意思,说明这是一个模块,但是我们目前在这里面什么也没做。

  • component

    是一个接口,Component是零件,组件的意思,说明这个接口的作用是连接各个组件,更形象的称法是注入器,前面必须加上@Component(modules = MainModule.class)注解,说明这是个是把MainModule模块提供的数据注入到目的地的

Andorid 动画

总的来说,Android动画可以分为两类,最初的传统动画和Android3.0 之后出现的属性动画
传统动画又包括 帧动画(Frame Animation)和补间动画(Tweened Animation)。

传统动画

帧动画

帧动画是最容易实现的一种动画,这种动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放,
从而在视觉上产生一种动画的效果;有点类似于某些软件制作gif动画的方式。

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/a_0"
android:duration="100" />
<item
android:drawable="@drawable/a_1"
android:duration="100" />
<item
android:drawable="@drawable/a_2"
android:duration="100" />
</animation-list>
1
2
3
4
5
6
7
8
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame_animation);
ImageView animationImg1 = (ImageView) findViewById(R.id.animation1);
animationImg1.setImageResource(R.drawable.frame_anim1);
AnimationDrawable animationDrawable1 = (AnimationDrawable) animationImg1.getDrawable();
animationDrawable1.start();
}

在有些代码中,我们还会看到android:oneshot=”false” ,这个oneshot 的含义就是动画执行一次(true)还是循环执行多次。

补间动画

补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)
补间动画的实现,一般会采用xml 文件的形式;代码会更容易书写和阅读,同时也更容易复用。

XML 实现

首先,在res/anim/ 文件夹下定义如下的动画实现方式

alpha_anim.xml 动画实现

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toAlpha="0.0" />

scale.xml 动画实现

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0"/>

然后,在Activity中

1
2
3
Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_anim);
img = (ImageView) findViewById(R.id.img);
img.startAnimation(animation);

这样就可以实现ImageView alpha 透明变化的动画效果。

也可以使用set 标签将多个动画组合(代码源自Android SDK API)

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
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@[package:]anim/interpolator_resource"
android:shareInterpolator=["true" | "false"] >
<alpha
android:fromAlpha="float"
android:toAlpha="float" />
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" />
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
<set>
...
</set>
</set>

可以看到组合动画是可以嵌套使用的。

各个动画属性的含义结合动画自身的特点应该很好理解,就不一一阐述了;这里主要说一下interpolatorpivot

Interpolator 主要作用是可以控制动画的变化速率 ,就是动画进行的快慢节奏。

Android 系统已经为我们提供了一些Interpolator ,比如 accelerate_decelerate_interpolator,accelerate_interpolator等。更多的interpolator 及其含义可以在Android SDK 中查看。同时这个Interpolator也是可以自定义的,这个后面还会提到。

pivot 决定了当前动画执行的参考位置

pivot 这个属性主要是在translate 和 scale 动画中,这两种动画都牵扯到view 的“物理位置“发生变化,所以需要一个参考点。而pivotX和pivotY就共同决定了这个点;它的值可以是float或者是百分比数值。

我们以pivotX为例,

pivotX取值 含义
10 距离动画所在view自身左边缘10像素
10% 距离动画所在view自身左边缘 的距离是整个view宽度的10%
10%p 距离动画所在view父控件左边缘的距离是整个view宽度的10%

pivotY 也是相同的原理,只不过变成的纵向的位置。如果还是不明白可以参考源码,在Tweened Animation中结合seekbar的滑动观察rotate的变化理解。

Activity切换动画实现

主要原理还是通过补间动画/过渡动画实现,Andorid 为此为我们提供了四个属性:

activityOpenEnterAnimation // 用于设置打开新的Activity并进入新的Activity展示的动画
activityOpenExitAnimation // 用于设置打开新的Activity并销毁之前的Activity展示的动画
activityCloseEnterAnimation // 用于设置关闭当前Activity进入上一个Activity展示的动画
activityCloseExitAnimation // 用于设置关闭当前Activity时展示的动画

具体实现:

1.在res/anim目录下创建好动画文件

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">

<translate
android:duration="500"
android:fromXDelta="-100.0%p"
android:toXDelta="0%p" />

</set>

2.在style文件中创建一个样式引入动画

1
2
3
4
5
6
<style name="activityAnimation" parent="@android:style/Animation">
<item name="android:activityOpenEnterAnimation">@anim/in_from_right</item>
<item name="android:activityOpenExitAnimation">@anim/out_to_left</item>
<item name="android:activityCloseEnterAnimation">@anim/in_from_left</item>
<item name="android:activityCloseExitAnimation">@anim/out_to_right</item>
</style>

3.将我们创建好的动画通过windowAnimationStyle将动画注入到主题中

1
<item name="android:windowAnimationStyle">@style/activityAnimation</item>

4.引入Mainfests.xml开启动画的主题

1
2
3
4
5
6
7
8
9
10
11
12
13
<application
android:name=".application.MyBaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_logo"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_logo"
android:supportsRtl="true"
android:theme="@style/BaseAppTheme"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning"

tools:replace="android:theme">

tip:这种方式有个缺点 有的机器虽然进入的动画是可用的,但是退出的动画无效,实现Activtiy切换动画效果的方式不止一种

android 在切换本地语言时会重新执行Activity的生命周期函数,即原来的activity会被销毁并创建一个新的activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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"
<!--在文件中加入忽略横屏和切换语言带来的影响-->
android:configChanges="locale|layoutDirection|orientation|screenSize|keyboardHidden" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />

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

自定义控件

LayoutInflater.from(context).inflate()

这个方法有几个重载方法,其中主要使用的参数简单的解释一下。

1 int resource 代表需要加载资源的id

2 ViewGroup root 代表资源需要被添加的地方

3 boolean attachToRoot 是否要被添加到root中

4 XmlPullParser parser 代表xml文件

1
2
3
4
5
6
7
       super(context, attrs, defStyleAttr);
// 绑定布局,引入子view
// View inflate = LayoutInflater.from(context).inflate(R.layout.num_key_pad, this, false);
// addView(inflate);
// 等价于 LayoutInflater.from(context).inflate(R.layout.num_key_pad, this);或者LayoutInflater.from(context).inflate(R.layout.num_key_pad, this,true);
// attachRoot的作用就是将view添加到viewgroup中,作用和addview类似
LayoutInflater.from(context).inflate(R.layout.num_key_pad, this);

参考连接:一篇弄懂LayoutInflater.from(context).inflate()

谢谢老板