模块化 vs 组件化
模块化:模块化就是将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容。
模块我们相对熟悉,比如登录功能可以是一个模块,搜索功能可以是一个模块,汽车的发送机也可是一个模块
Dalvik指令集是基于寄存器的架构,执行特有的文件格式——dex字节码(适合内存和处理器速度有限的系统)。而JVM是基于栈的。
相对于基于栈的JVM而言,基于寄存器的Dalvik VM实现虽然牺牲了一些平台无关性,但是它在代码的执行效率上要更胜一筹。
总结:
View绘制分三个步骤,顺序是:onMeasure,onLayout,onDraw。经代码亲测,log输出显示:调用invalidate方法只会执行onDraw方法;调用requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法。
所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法。
相关知识点:
1.invalidate和postInvalidate:invalidate方法只能用于UI线程中,在非UI线程中,可直接使用postInvalidate方法,这样就省去使用handler的烦恼。
在网站日志中,我们经常会看到很多返回的http代码,如201、304、404、500等等。可是这些具体的返回的HTTP代码究竟什么含义呢,在此做一下知识普及吧,记不住不要紧,到时候看看就行了,但最主要的几个还要要清楚的。
一些常见的状态码为:
200 - 服务器成功返回网页
404 - 请求的网页不存在
503 - 服务器超时
下面提供 HTTP 状态码的完整列表。点击链接可了解详情。您也可以访问 HTTP 状态码上的 W3C 页获取更多信息。
1xx(临时响应)
表示临时响应并需要请求者继续执行操作的状态码。
100(继续)请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
101(切换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。
2xx (成功)
表示成功处理了请求的状态码。
200(成功)服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。如果是对您的 robots.txt 文件显示此状态码,则表示 Googlebot 已成功检索到该文件。
201(已创建)请求成功并且服务器创建了新的资源。
202(已接受)服务器已接受请求,但尚未处理。
203(非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源。
204(无内容)服务器成功处理了请求,但没有返回任何内容。
205(重置内容)服务器成功处理了请求,但没有返回任何内容。与 204 响应不同,此响应要求请求者重置文档视图(例如,清除表单内容以输入新内容)。
206(部分内容)服务器成功处理了部分 GET 请求。
3xx (重定向)
要完成请求,需要进一步操作。通常,这些状态码用来重定向。Google 建议您在每次请求中使用重定向不要超过 5 次。您可以使用网站管理员工具查看一下 Googlebot 在抓取重定向网页时是否遇到问题。诊断下的网络抓取页列出了由于重定向错误导致 Googlebot 无法抓取的网址。
300(多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301(永久移动)请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。您应使用此代码告诉 Googlebot 某个网页或网站已永久移动到新位置。
302(临时移动)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个网页或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。
303(查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。对于除 HEAD 之外的所有请求,服务器会自动转到其他位置。
304(未修改)自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
如果网页自请求者上次请求后再也没有更改过,您应将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。服务器可以告诉搜索引擎的蜘蛛/机器人 自从上次抓取后网页没有变更,进而节省带宽和开销。
.
305(使用代理)请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
307(临时重定向)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个页面或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。
4xx(请求错误)
这些状态码表示请求可能出错,妨碍了服务器的处理。
400(错误请求)服务器不理解请求的语法。
401(未授权)请求要求身份验证。对于登录后请求的网页,服务器可能返回此响应。
403(禁止)服务器拒绝请求。如果您在 Googlebot 尝试抓取您网站上的有效网页时看到此状态码(您可以在 Google 网站管理员工具诊断下的网络抓取页面上看到此信息),可能是您的服务器或主机拒绝了 Googlebot 访问。
404(未找到)服务器找不到请求的网页。例如,对于服务器上不存在的网页经常会返回此代码。
如果您的网站上没有 robots.txt 文件,而您在 Google 网站管理员工具"诊断"标签的 robots.txt 页上看到此状态码,则这是正确的状态码。但是,如果您有 robots.txt 文件而又看到此状态码,则说明您的 robots.txt 文件可能命名错误或位于错误的位置(该文件应当位于顶级域,名为 robots.txt)。
如果对于 Googlebot 抓取的网址看到此状态码(在"诊断"标签的 HTTP 错误页面上),则表示 Googlebot 跟随的可能是另一个页面的无效链接(是旧链接或输入有误的链接)。
405(方法禁用)禁用请求中指定的方法。
406(不接受)无法使用请求的内容特性响应请求的网页。
407(需要代理授权)此状态码与 401(未授权)类似,但指定请求者应当授权使用代理。如果服务器返回此响应,还表示请求者应当使用代理。
408(请求超时)服务器等候请求时发生超时。
409(冲突)服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息。服务器在响应与前一个请求相冲突的 PUT 请求时可能会返回此代码,以及两个请求的差异列表。
410(已删除)如果请求的资源已永久删除,服务器就会返回此响应。该代码与 404(未找到)代码类似,但在资源以前存在而现在不存在的情况下,有时会用来替代 404 代码。如果资源已永久移动,您应使用 301 指定资源的新位置。
411(需要有效长度)服务器不接受不含有效内容长度标头字段的请求。
412(未满足前提条件)服务器未满足请求者在请求中设置的其中一个前提条件。
413(请求实体过大)服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414(请求的 URI 过长)请求的 URI(通常为网址)过长,服务器无法处理。
415(不支持的媒体类型)请求的格式不受请求页面的支持。
416(请求范围不符合要求)如果页面无法提供请求的范围,则服务器会返回此状态码。
417(未满足期望值)服务器未满足"期望"请求标头字段的要求。
5xx(服务器错误)
这些状态码表示服务器在处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。
500(服务器内部错误)服务器遇到错误,无法完成请求。
501(尚未实施)服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
502(错误网关)服务器作为网关或代理,从上游服务器收到无效响应。
503(服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
504(网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505(HTTP 版本不受支持)服务器不支持请求中所用的 HTTP 协议版本。
java.io.EOFException 表示输入过程中意外地到达文件尾或流尾的信号,导致异常。
看你的代码,估计是socket没有正确创建,建议调用之前先检查socket的状态
是因为阻塞引起的,因为server的input.read读不到东西就会阻塞,当你关掉client的时候,server就知道没有东西进来了,所以就报了个异常,其实这个是正常的,
只是告诉你,该把socket关闭一下,还有input也关闭一下
2、Android画线的时候,如果用到path进行画线,如果想实现画不同颜色或其他效果时,要注意使用不同的Path对象,不然后面的设置会覆盖前面的,也就是说,
调用canvas.drawPath(path, paint)时,会在同一个path对象的所有路径上进行paint改变后的绘制,也就相当于把前面的覆盖了。
从一个App跳转至另一个App的写法:
try {
PackageManager packageManager = getPackageManager();
Intent intent=new Intent();
intent = packageManager.getLaunchIntentForPackage("com.tencent.mm");
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
Intent viewIntent = new
Intent("android.intent.action.VIEW", Uri.parse("http://weixin.qq.com/"));
startActivity(viewIntent);
}
从一个App跳转至另一个App指定的Activity的写法:
try {
Intent mIntent = new Intent();
ComponentName mComp = new ComponentName("cn.com.qrun.ph25.mobi.qr104",
"cn.com.qrun.ph25.mobi.report.activity.BpReportActivity");//注意AcitivityName(目标应用程序)要完整的,带包名的PackageName的
mIntent.setComponent(mComp);
mIntent.setAction(Intent.ACTION_VIEW);
// mIntent.putExtra("user", edtAccount.getText().toString());
// mIntent.putExtra("pwd", edtPwd.getText().toString());
startActivity(mIntent);
}catch (Exception e){
Log.i("hmy","未安装xxxApp");
}
不换行且要加逗号的用法是:
先将文本复制到 word文档中,然后Ctrl+F 查找内容是:^p 替换为: ,^p 然后 在Ctrl+F 查找内容是: ^p 替换为:(空格)
Android 蓝牙连接实现:
1. AndroidManifest中添加蓝牙管理权限
2. 打开蓝牙
打开蓝牙有两种方式,发送蓝牙开启请求或代码后台自动打开蓝牙。
2.1 发送蓝牙开启请求
先判断BluetoothAdapter是不是为空,为空有可能是系统没有蓝牙模块,再判断蓝牙的状态是不是开启的,不是开启的就发送请求。
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent)
}
2.2 后台自动打开蓝牙
if (btAdapter != null && !btAdapter.isEnabled()) {
btAdapter.enable();
}
3. 获取已经绑定(配对)的设备
Set
BluetoothDevice有如下方法:
getBondState() //获取设备绑定状态,BOND_NONE(未绑定), BOND_BONDING(正在绑定), BOND_BONDED(已绑定)
getType() //获取蓝牙类型,DEVICE_TYPE_CLASSIC(普通类型BR/EDR), DEVICE_TYPE_LE(低功耗类型), DEVICE_TYPE_DUAL(双类型BR/EDR/LE), DEVICE_TYPE_UNKNOWN(未知)
getAddress() //蓝牙地址
getBluetoothClass() //获取蓝牙类别(BluetoothClass),如手机、电脑、耳机,注意与蓝牙类型的区别
getName() //蓝牙设备名字
BluetoothClass蓝牙类别说明:
目前有12个大类,定义在BluetoothClass.Device.Major下
BITMASK、MISC、COMPUTER、PHONE、NETWORKING、AUDIO_VIDEO、PERIPHERAL、IMAGING、WEARABLE、TOY、HEALTH、UNCATEGORIZED
每个大类有若干个小类别,定义在BluetoothClass.Device下
如: AUDIO_VIDEO_WEARABLE_HEADSET、AUDIO_VIDEO_HANDSFREE、AUDIO_VIDEO_RESERVED、AUDIO_VIDEO_MICROPHONE、AUDIO_VIDEO_LOUDSPEAKE
4. 监听蓝牙设备的变化
// 监听蓝牙设备的变化
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND); //发现新设备
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //绑定状态改变
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //开始扫描
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //结束扫描
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); //连接状态改变
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //蓝牙开关状态改变
BluetoothReceiver receiver = new BluetoothReceiver();
mActivity.registerReceiver(receiver, filter);
...
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch(intent.getAction()) {
case BluetoothAdapter.ACTION_FOUND:
//获取扫描到的设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//判断扫描到的设备是不是在已绑定设备列表中,在就跳过
for (BluetoothDevice d: bondedDevices) {
if (d.getAddress().equals(device.getAddress())) {
return;
}
}
mScannedDevices.add(device);
break;
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
//开始扫描
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
//结束扫描
break;
case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
//绑定状态改变
break;
case BluetoothAdapter.ACTION_STATE_CHANGED:
//蓝牙开关状态改变
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
break;
case BluetoothAdapter.STATE_ON:
break;
}
break;
}
}
}
5. 绑定蓝牙设备
绑定比较简单,调用 BluetoothDevice的createBond就一句代码,调用后就开始尝试绑定。
private boolean createBond(BluetoothDevice device) {
return device.createBond();
}
绑定成功后会触发刚注册的蓝牙监听器,action为 ACTION_BOND_STATE_CHANGED
6. 取消绑定
BluetoothDevice绑定的方法是开放的,取消绑定的方法却是隐藏的,只对系统app开放,坑爹,只能用反射来解决了。
private boolean removeBond(BluetoothDevice device) {
Class btDeviceCls = BluetoothDevice.class;
Method removeBond = btDeviceCls.getMethod("removeBond");
removeBond.setAccessible(true);
return (boolean) removeBond.invoke(device);
}
7. 连接
绑定(配对)和连接是两个不同的过程,绑定是指两个设备发现了对方的存在,可以获取到对方的名称、地址等信息,有能力建立起连接;连接是指两个设备共享了一个RFCOMM通道,有能力进行数据互传。确认绑定上了之后,才能开始连接。可以试试蓝牙音箱的连接过程,就是先点击一次,开始配对,配对成功后出现在已绑定的列表中,再点击一次,就开始连接,连接成功后蓝牙音箱就有声音了。
这一步我吃了不少亏,网上搜到的都是下面的这种连接方式
UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private void connect(BluetoothDevice device) {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
socket.connect();
...
socket.close();
}
这种连接是用蓝牙来进行Socket通信的,而我要做的是如何把手机上连上蓝牙耳机或蓝牙音箱,这种方式是不行的。
折腾了好久,最后找到了下面的连接方法,专门针对AUDIO、VIDEO类型的蓝牙设备的连接。
if (device.getBluetoothClass().getMajorDeviceClass() != BluetoothClass.Device.Major.AUDIO_VIDEO) {
return;
}
btdapter.getProfileProxy(context, new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
BluetoothHeadset bluetoothHeadset = (BluetoothHeadset) proxy;
Class btHeadsetCls = BluetoothHeadset.class;
try {
Method connect = btHeadsetCls.getMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(bluetoothHeadset, device);
} catch (Exception e) {
Log.e(TAG, e + "");
}
}
@Override
public void onServiceDisconnected(int profile) {
}
}, BluetoothProfile.HEADSET);
(2)解析Json对象:
String json = "{\n" +
"\t\"id\":2, \"name\":\"金鱼\", \n" +
"\t\"price\":12.3, \n" +
"\t\"imagePath\":\"http://blog.csdn.net/qq_29269233/L05_Server/images/f1.jpg\"\n" +
"}\n";
ShopInfo shopInfo = null;
try {
JSONObject jsonObject = new JSONObject(json);
// int id = jsonObject.getInt("id");
int id1 = jsonObject.optInt("id");
String name = jsonObject.optString("name");
double price = jsonObject.optDouble("price");
String imagePath = jsonObject.optString("imagePath");
// 封装Java对象
shopInfo = new ShopInfo(id1, name, price, imagePath);
} catch (JSONException e) {
e.printStackTrace();
}
JSONObject 数组或者集合解析
Object datas= MobiDataSyncService.getReturnValue(message);
try {
getTestData(datas);
} catch (JSONException e) {
e.printStackTrace();
}
private void getTestData(Object datas) throws JSONException {
String jsonStr= JSONUtil.objectToJSONStr(datas);
JSONArray jsonArray = new JSONArray(jsonStr);
Log.i("hmy","jsonObject数据------- "+jsonStr);
List
testBpBeanList.clear();
for (int i=0;i
TestBpBean bpBean=new TestBpBean();
bpBean.setId(ob.getString("id"));
bpBean.setTimePoint(ob.getString("timePoint"));
bpBean.setSbp(ob.getString("sbp"));
bpBean.setDbp(ob.getString("dbp"));
bpBean.setCheckDate(ob.getString("checkDate"));
testBpBeanList.add(bpBean);
}
Log.i("hmy","jsonObject解析数据结果------- "+testBpBeanList.size()+" "+testBpBeanList.toString());
}
I Intent与IntentFilter
Intent促进了组件之间的交互,这对于开发者非常重要,而且它还能做为消息的载体,去指导组件做出相应的行为,也就是说Intent可以携带数据,传递给Activity/Service/BroadcastReceiver。
1.启动Activity。Activity可以简单的理解为手机屏幕中的一个页面,你可以通过将Intent传入startActivity方法来启动一个Activity的实例,也就是一个页面,同时,Intent也可以携带数据,传递给新的Activity。如果想要获取新建的Activity执行结果,可以通过onActivityResult()方法去启动Activity。
2.启动Service。Service是一个不呈现交互画面的后台执行操作组件,可以通过将Intent穿入startService()方法来启动一个Service来启动服务。
3.传递广播BroadCast。广播是任何应用都可以接收到的消息,通过将Intent传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast()方法,可以将广播传递接收方
II Intent类型
在Android中,Intent分为两种类型,显式和隐式
1.显式Intent,可以通过类名来找到相应的组件,在应用中用显式Intent去启动一个组件,通常是因为我们知道这个组件(Activity或者Service)的名字。如下代码,我们知道具体的Activity的名字,要启动一个新的Activity,下面就是用的显示Intent。
Intent intent = new Intent(context,XXActivity.class);
startActivity(intent);
2.隐式Intent,不指定具体的组件,但是它会声明将要执行的操作,从而匹配到相应的组件。最简单的Android中调用系统拨号页面准备打电话的操作,就是隐式Intent。
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri data = Uri.parse("tel:" + "135xxxxxxxx");
intent.setData(data);
startActivity(intent);
使用显示Intent去启动Activity或者Service的时候,系统将会立即启动Intent对象中指定的组件。
使用隐式Intent的时候,系统通过将Intent对象中的IntentFilter与组件在AndroidManifest.xml或者代码中动态声明的IntentFilter进行比较,从而找到要启动的相应组件。如果组件的IntentFilter与Intent中的IntentFilter正好匹配,系统就会启动该组件,并把Intent传递给它。如果有多个组件同时匹配到了,系统则会弹出一个选择框,让用户选择使用哪个应用去处理这个Intent,比如有时候点击一个网页链接,会弹出多个应用,让用户选择用哪个浏览器去打开该链接,就是这种情况。
IntentFilter通常是定义在AndroidManifest.xml文件中,也可以动态设置,通常是用来声明组件想要接受哪种Intent。例如,你如果为一个Activity设置了IntentFilter,你就可以在应用内或者其他应用中,用特定的隐式Intent来启动这个Activity,如果没有为Activity设置IntentFilter,那么你就只能通过显示Intent来启动这个Activity。
注意,为了确保系统的稳定性,官方建议使用显示Intent来启动Service,同时也不要为Service设置IntentFilter,因为如果使用隐式Intent去启动Service,我们并不知道那些服务会响应Intent,而且由于服务大多是不可见的,我们也不知道那些服务被启动了,这是非常危险的。在Android 5.0(API 21)以后,如果使用隐式的Intent去调用bindService()方法,系统会抛出异常。
III Intent的属性
Intent作为消息的载体,系统根据它去决定启动哪个具体的组件同时将组件执行中需要的信息传递过去。Intent能够包含的属性有Component、Action、Data、Category、Extras、Flags
Component,要启动的组件名称。这个属性是可选的,但它是显式Intent的一个重要属性,设置了这个属性后,该Intent只能被传递给由Component定义的组件。隐式Intent是没有该属性的,系统是根据其他的信息(例如,Action、Data等)来判断该Intent应该传递给哪个组件。这个属性是目标的组件的具体名称(完全限定类名),例如,com.example.DemoActivity。该属性可以通过setComonentName()、setClass()、setClassName()或者Intent的构造函数来设置。
Action,表明执行操作的字符串。它会影响Intent的其余信息,比如Data、Extras。该属性可以通过setAction()方法或者Intent的构造函数来设置。用户可以自定义这个属性,也可以使用系统中已经有的Action值。下面列出启动Activity时候的一些通用Action属性。
ACTION_VIEW,当有一些信息需要展示出来,可以设置Intent的Action为这个值,并调用startActivity()方法
ACTION_SEND,当用户有一些信息需要分享到其他应用,可以设置Intent的Action为这个值,并调用startActivity()方法
ACTION_DIAL,拨打电话,可以设置Intent的Action为这个值,并调用startActivity()方法
ACTION_EDIT,编辑某些文件,可以设置Intent的Action为这个值,并调用startActivity()方法
关于父类的静态方法能否被子类重写以及原因:
1.子类不能重写父类的静态方法
2.子类继承父类后,用相同的静态方法和非静态方法,这时非静态方法覆盖父类的方法 这叫方法重写
父类的静态方法被隐藏(如果对象是父类则调用该隐藏的静态方法),另外子类可继承父类的静态与非静态方法
3."重写"只能适用于实例方法.不能用于静态方法.对于静态方法,只能隐藏(形式上被重写了,但是不符合的多态的特性),“重写”是用来实现多态性的,只有实例方法是可以实现多态,而静态方法无法实现多态
子类可以继承父类的静态方法 但是不能重写
方法重载和重写的区别:
重载
1.方法重载 是让类型以统一的方式处理不同类型数据的一种手段 多个同名函数同时存在,具有不同的参数个数/类型 重载Overloading是一个类中多态性的一种表现
2.Java的方法重载,就是在类中以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义 调用方法时通过传递给它们的不同参数个数和参数类型
来决定具体使用哪个方法, 这就是多态性
3.重载的时候 方法名要一样 但是参数类型和个数不一样 返回值类型可以相同也可以不相同 无法返回型别作为 重载函数的 区别标准
重写
1.父类与子类之间的多态性 对父类的函数进行重新定义 如果在子类中定义某 方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)
在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖
2.若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键 字引用了当前类的父类
3.子类函数的访问修饰权限不能少于父类的
垃圾收集算法核心思想
1.Java语言提供了自动的GC机制,系统会经常检查内存,采用对象引用计数的方式 将引用次数为0的对象回收 这样可以防止两个危险:
(1)防止无用对象占用资源
(2)防止有用对象被释放,引起内存非法引用
2.出发GC(Garbage Collector)的条件
(1)应用进程空闲的时候,GC会回收空闲进程的内存资源
(2)应用进程繁忙的时候,当需要的内存资源不足的时候, GC会强制执行回收优先级比较低的进程资源,如果还是不足,则再回收两次,还是不足则会报OOM
3.减少GC开销的措施
(1)尽量少显示的调用System.gc();
(2)减少临时对象的引用
(3)对象使用完后 设置为null 这样会方便系统查找空对象 更快回收掉内存
(4)能用int等基本数据类型,就尽量不要使用Integer等引用类型 基本类型占用的资源比引用类型要小得多
(5)尽量少地使用static变量。static 变量是全局性的,系统在堆中为其分配内存,GC无法回收该内存;
(6)对于需要使用变长的字符串变量,尽量使用StringBuffer而不是String。String每赋值一次,就会重新分配一次内存,
String str = str1+str2+str3+str4+str5,每多一个“+”,就会创建一个对象。
(7)分散创建和删除对象的空间,一次性删除或者创建太多对象 会造成内存突然变的紧张或者一次性释放太多 不利于内存的合理使用
总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:
1.给对象赋予了空值null,以下再没有调用过。
2.给对象赋予了新值,既重新分配了内存空间。
最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集
JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。
总的来说,有两个条件会触发主GC:
①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。
②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。
若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。
由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的
Android进程和线程的区别
(1)在Android APP中,只允许有一个主线程,进行UI的渲染等等,但是不能进行耗时操作(网络交互等等),否则会造成ANR,就是线程阻塞卡死,未响应。
(2)除了主线程之外,耗时操作都应该规范到子线程中,线程之间会有相应的通信方式,但相互独立。
(3)线程是进程的一个实体 是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程比进程更小 基本上不拥有系统资源 故对它的调度
所用资源小,能更高效的提高系统内存多个程序间并发执行的
进程是系统进行资源分配和调度的一个独立单位 可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体,是一个“执行中的程序”。不只是程序的代码,还包括当前的活动
子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文
进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见
进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性
线程上下文切换比进程上下文切换要快得多。
**********************************************************************************************************************************************
2018-04-04 物理返回键onKeyDown 的解析
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0){
System.out.println("返回键执行了!!!");
}
return true;
}
解决方案:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0){
System.out.println("返回键执行了!!!");
}
return false;
}
详细描述:返回true是表示该次事件已经处理完毕,如果false表示事件没有处理 一般是递交给super 方法处理。这时候系统就根据back原来的功能
退出程序,在做闪屏的时候 会用到这个更改返回值.
关于Activity中按下音量加减键导致页面重新加载的问题 需要拦截下该事件 通过重写onKeyDown方法
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
return false;
}
return false;
}
**********************************************************************************************************************************************
volatile关键字的意义作用
1.可见性
Java提供了volatile关键字来保证可见性
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时 它会去内存中读取新值
而普通的共享变量不能保证可见性 因为普通共享变量被修饰后 什么时候被写入主存是不确定的 当其他线程去读取时 此时内存中可能还是原来的旧值 因此无法
保证可见性。 另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,
并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
2.有序性
在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
记住三个要点
1.使用volatile关键字会强制将修改的值立即写入主存
2.使用volatile关键字的话 当线程2进行修改时 会导致线程1的工作内存中缓存变量stop的缓存行无效 也就是执行线程1的CPU缓存中的stop无效
3.由于线程1的工作内存中缓存变量stop的缓存行无效 所以线程1再次读取变量stop的值时会去主存读取
*************************************************************************************************************************************
Collections.synchronizedList
如果多个线程同时访问一个ArrayList实例 而其中至少一个线程从结构上修改了列表 那么它就必须保持外部同步(结构上修改是指任何添加或者删除一个或多个元素的操作
或者显式调整底层数组的大小 仅仅设置元素的值不是结构上的修改) 这一般通过对自然封装该列表的对象进行同步操作来完成
如果不存在这样的对象,则应该使用Collections.synchronizedList方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问
返回由指定列表支持的同步(线程安全的)列表。为了保证按顺序访问,必须通过返回的列表完成对底层列表的所有访问。
虽然是ArrayList是线程不安全的,但是通过Collections.synchronizedList()方法可以将线程不安全的List转成线程安全的List
*******************************************************************************************************************************
Activity作为入参传入方法
//方法定义
private void setIntent(Class
Intent intent=new Intent(this,activity);
startActivity(intent);
}
//方法调用
setIntent(TestActivity.class);
*******************************************************************************************************************************
Cookie和Session都为了用来保存状态信息,都是保存客户端状态的机制,它们都是为了解决HTTP无状态的问题而所做的努力。
Session可以用Cookie来实现,也可以用URL回写的机制来实现。
Cookie和Session有以下明显的不同点:
1.Cookie将状态保存在客户端,Session将状态保存在服务器端
2.Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器. 网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些Cookies
并将它们保存为一个本地文件 他会自动将同一个服务器的任何请求附上这些cookies
3.Session是针对每一个用户的 变量的值保存在服务器上 用一个sessionID来区分是不同用户session变量,这个值是通过用户的浏览器在访问时候返回给服务器 当客户禁用
cookie时 这个值也可能设置为由get来返回给服务器
4.就安全性来说:当你访问一个使用session 的站点,同时在自己机器上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。
*******************************************************************************************************************************
Android 网络请求框架及其特点
1.Volley
2.OKhttp
3.Retrofit
Retrofit是基于Okhttp封装的一套RESTful网络请求框架 底层默认采用的Okhttp 所以网络请求框架就剩下两个了
1.Volley
是Google官方推出的一套小而巧的异步请求库,该框架封装的扩展性很强 支持HttpClient、HttpURLConnection, 甚至支持OkHttp 在Android 6.0以上的机型中不在支持HttpClient
Volley 在Android2.3以下机型使用的是HttpClient 在Android2.3及以上默认使用HttpURLConnection 也就是说Volley 是在HttpUrlConnection的基础上进行的封装,这点与OKhttp不同,一会会说到。
问题1:它有什么优势?
答: 基于网络队列 适合小数据频繁通信 Volley的网络请求线程池默认大小为4 意味着可以并发进行4个请求 大于4个的 会排在队列中
问题2:为什么Volley只适合数据量小 并发高的请求?为什么Volley不适合POST大量数据 以及为什么不适合上传下载大量文件?
答: Volley在内部实现上采用了ByteArrayPool进行内存中的数据存储 ByteArryPool只是一个小于4K的内存缓存池 当存储的时候 尤其是从ByteArrayPool中取出一块已经
分配的内存区域 不必每次存数据都要进行内存分配 而是先查找缓存池中有无适合的内存区域 如果有 直接拿来用 从而减少内存分配的次数
但是这块内存的大小有限制 所以当进行上传或者下载大量数据的时候这块内存容易溢出 造成OOM
问题3:那它怎么操作文件?
答:通过配置 它是可以完成大文件操作的 方法请自行百度
2.OKhttp
这个框架比较火,估计做Android开发的都知道
OKhttp是高性能的http库,支持同步、异步,而且实现了spdy、http2、websocket协议,api很简洁易用,和Volley一样实现了http协议的缓存。
它与Volley的区别之一是Volley底层使用HttpURLConnection 而Okhttp则重新实现了http协议 并又有底层依赖
它的优势一大堆 目前Android 源码底层已经用Okhttp代替了HttpURLConnection 可见Okhttp还是十分强大的
后续可往上补充
*****************************************************************************************************************************************************************
java 数据进制
十进制转十六进制
十六进制转换有16进制每一位上可以是从小到大为0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F16个大小不同的数,即逢16进1,
其中用A,B,C,D,E,F(字母不区分大小写)这六个字母来分别表示10,11,12,13,14,15
十六进制数是在程序设计时经常要使用到的一种整数的表示方式。它有0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F共16个符号,分别表示十进制数的0至15。
十六进制的计数方法是满16进1,所以十进制数16在十六进制中是10,而十进制的17在十六进制中是11,以此类推,十进制的30在十六进制中是1E。
例:2AF5换算成10进制:
直接计算就是:
5 * 16^0 + F * 16^1 + A * 16^2 + 2 * 16^3 = 10997
java代码
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Integer n = in.nextInt();
in.close();
System.out.println(Integer.toHexString(n).toUpperCase());
}
}
******************************************************************************************
android Fragment中onViewCreated 和onCreateView的区别
onCreateView是创建的时候调用,onViewCreated是在onCreateView后被触发的事件,前后关系
就是fragment中的onCreateView和onViewCreated的区别和联系。
且onStart运行时间位于onViewCreated之后
*****************************************************************************************************
GsonFormat的使用
Alt+Insert 或者 Alt+S 然后粘贴上json代码 点击OK即可
*************************************************************************
.bin 文件
Bin文件,也就是二进制文件。Bin文件只是一种文件格式,个人电脑中常用的Bin文件一般是虚拟光驱文件,用于保存虚拟光盘镜像。
bin是英语binary的简写,意为二进制。后缀名为.bin的文件, 只是表明其文件格式是binary格式,但是不能仅由此确定该文件的内容和用途。
很多程序和软件都可以创建或者使用bin文件,如虚拟光驱可以使用bin文件,而DOS下汇编语言编译产生的文件也可以是bin文件。这两种bin文件虽然后缀名相同,但是是不可以通用的。
*****************************************************************************************************
Android 6.0动态权限申请
1.权限等级和权限组
权限主要分为normal、dangerous、signature和signatureOrSystem四个等级,常规情况下我们只需要了解前两种,即正常权限和危险权限
1)正常权限
正常权限涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。应用声明其需要正常权限,系统会自动授予该权限。例如设置时区,只要应用声明过权限,
系统就直接授予应用此权限。下面是截止到API 23的普通权限(需访问)
ACCESS_LOCATION_EXTRA_COMMANDS ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY ACCESS_WIFI_STATE
BLUETOOTH BLUETOOTH_ADMIN
BROADCAST_STICKY CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE CHANGE_WIFI_STATE
DISABLE_KEYGUARD EXPAND_STATUS_BAR
GET_PACKAGE_SIZE INSTALL_SHORTCUT
INTERNET KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS NFC
READ_SYNC_SETTINGS READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS REQUEST_INSTALL_PACKAGES
SET_ALARM SET_TIME_ZONE
SET_WALLPAPER SET_WALLPAPER_HINTS
TRANSMIT_IR UNINSTALL_SHORTCUT
USE_FINGERPRINT VIBRATE
WAKE_LOCK WRITE_SYNC_SETTINGS
危险权限
危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如读取用户联系人,在6.0以上系统中,需要在运行时明确向用户申请权限。
2)权限组
系统根据权限用途又定义了权限组,每个权限都可属于一个权限组,每个权限组可以包含多个权限。例如联系人权限组,包含读取联系人、修改联系人和获取账户三个权限
如果应用申请访问一个危险权限,而此应用目前没有对应的权限组内的任何权限,系统会弹窗提示用户要访问的权限组(注意不是权限)。例如无论你申请READ_CONTACTS还是WRITE_CONTACTS,都是提示应用需要访问联系人信息。
* 如果用户申请访问一个危险权限,而应用已经授权同权限组的其他权限,则系统会直接授权,不会再与用户有交互。例如应用已经请求并授予了READ_CONTACTS权限,那么当应用申请WRITE_CONTACTS时,系统会立即授予该权限。下面为危险权限和权限组:
CALENDAR READ_CALENDAR
WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
********************************************************************************************
广播的发送和接收
正常来说应该是先注册广播接收者 在去发送广播 否则会出现广播发送之后 收不到广播的情况
//注册广播接受者
在onResume中注册 在onDestroy中销毁
private class FatReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("hmy","onReceive------------ ");
if(intent.getAction().equals(getPackageName()+Contants.FAT_SAVE_TASK)){
item=intent.getExtras().getString("item");
recordId=intent.getExtras().getString("recordId");
Log.i("hmy","体脂接收处--------- "+recordId+" item--- "+item);
queryUnormalData(item,Long.parseLong(recordId));
}
if(intent.getAction().equals(Contants.FAT_SAVE_TASK)){
String a=intent.getExtras().getString("item");
String b=intent.getExtras().getString("recordId");
Log.i("hmy","测试体脂接收处item--------- "+a+" recordId--- "+b);
}
}
}
@Override
protected void onResume() {
super.onResume();
//注册广播
IntentFilter filter=new IntentFilter();
filter.addAction(getPackageName()+Contants.FAT_SAVE_TASK);
// filter.addAction(Contants.FAT_SAVE_TASK);
receiver=new FatReceiver();
registerReceiver(receiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(this.receiver!=null){
this.unregisterReceiver(receiver);
}
}
//发送广播
Intent fatIntent = new Intent(getPackageName()+Contants.FAT_SAVE_TASK);//getPackageName()+Contants.FAT_SAVE_TASK: 意图(接收广播的地方 就根据这个值来判定是哪个广播)
fatIntent.putExtra("item","3,9");
fatIntent.putExtra("recordId",errormsg);
sendBroadcast(fatIntent);
*******************************************************************************************
为了能够提高读写效率,一次性把数据写、读。我们采用DataOutputStream。
针对file的写、读,我们使用DataOutputStream装饰FileOutputStream;
针对byte的写读,我们使用DataOutputStream装饰ByteArrayOutputStream。
************************************************************************************************
使用WebView 导致内存泄露的解决办法
1.https://my.oschina.net/zhibuji/blog/100580
2.https://blog.csdn.net/self_study/article/details/55046348
*******************************************************************************************************
App的应用签名生成方法:
打开命令符:cmd
敲入 keytool -list -v -keystore 你的keyStore文件
按下回车
提示输入密钥库口令: 输入keyStore的密码
敲回车 即可看到
MD5后面的一长串 就是签名 (把大写字母改为小写字母即可)
Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保留所有权利。
cmd命令行如下:
*******************************************
C:\Users\qrun1002>e:
E:\>keytool -list -v -keystore qrun-1.0.keystore
输入密钥库口令:(此处输入keystore的密码 且是不可见状态 输入就行了)
密钥库类型: JKS
密钥库提供方: SUN
您的密钥库包含 1 个条目
别名: qrun
创建日期: 2012-4-23
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: CN=QRun Inc., OU=Company, O=Computer, L=NanJing, ST=JiangSu, C=210000
发布者: CN=QRun Inc., OU=Company, O=Computer, L=NanJing, ST=JiangSu, C=210000
序列号: 4f952037
有效期开始日期: Mon Apr 23 17:26:15 CST 2012, 截止日期: Fri Apr 17 17:26:15 CST
2037
证书指纹:
MD5: 15:72:A8:B1:07:5E:39:4B:C9:7A:86:C6:B3:52:4C:6A
SHA1: D9:7D:07:86:16:B7:BB:92:51:BB:B2:D9:B2:87:47:52:BA:09:4A:51
SHA256: 2F:90:38:6B:2F:4F:BA:AE:3F:B6:C5:4F:ED:E8:BF:B3:80:C4:60:13:CF:
1A:14:00:F2:3B:B3:03:47:48:A8:A8
签名算法名称: SHA1withRSA
版本: 3
*******************************************
MD5 后面的一长串 就是签名文件 把冒号: 去掉 大写字母转成小写字母 即可
例如: 15:72:A8:B1:07:5E:39:4B:C9:7A:86:C6:B3:52:4C:6A
转完后: 1572a8b1075e394bc97a86c6b3524c6a(即为签名)