这篇文章将为大家详细讲解有关微信小程序中wx.request如何实现封装,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。看项目代码时发现了下面几点问题:网络请求都写在Page里,每个请求都要重复的写wx.request以及一些基础配置;每个页面里都要处理相同类型的异常;后端返的http status code为200以外时,并不能直接进入fail对应函数进行处理;针对这些问题,首先在项目目录里新建了一个apis的目录,把所有与API请求的东西都放在这个目录里,如下图这样。1. 新建一个request类,对wx.request进行简单封装 在request类里做了以下几件事:在构造函数里创建默认请求的http header,可以在header里配制一些内容,在对应请求方法中如果没有设置header参数,就使用此默认header参数;以get post delete put等方法对request进行封装,在发起网络请求不需要重复的写wx.request({method:xxx})这些代码,只要调用getRequest、postRequest等方法就可以了;在rquest的结果返回处理函数success中,判定服务端返回的状态代码,对于200状态代码的按业务处理成功处理,对于非200的状态码按异常处理。预留统一异常处理函数处理接口,可以通过setErrorHandler来设置统一的异常处理,这样对于一些可以统一处理的异常就不用在业务页面里去重复处理了,例如后端返回401的代码,就可以统一转到登录页面让用户登录了;此request不限定服务提供都,可以是自己开发的业务服务端,也可以用于第三方服务的调用;/** * name: api.js * description: request处理基础类 * author: 徐磊 * date: 2018-5-19 */class request { constructor() { this._header = {} }/** * 设置统一的异常处理 */ setErrorHandler(handler) { this._errorHandler = handler; } /** * GET类型的网络请求 */ getRequest(url, data, header = this._header) { return this.requestAll(url, data, header, 'GET') } /** * DELETE类型的网络请求 */ deleteRequest(url, data, header = this._header) { return this.requestAll(url, data, header, 'DELETE') } /** * PUT类型的网络请求 */ putRequest(url, data, header = this._header) { return this.requestAll(url, data, header, 'PUT') } /** * POST类型的网络请求 */ postRequest(url, data, header = this._header) { return this.requestAll(url, data, header, 'POST') } /** * 网络请求 */ requestAll(url, data, header, method) { return new Promise((resolve, reject) => { wx.request({ url: url, data: data, header: header, method: method, success: (res => { if (res.statusCode === 200) { //200: 服务端业务处理正常结束 resolve(res) } else { //其它错误,提示用户错误信息 if (this._errorHandler != null) { //如果有统一的异常处理,就先调用统一异常处理函数对异常进行处理 this._errorHandler(res) } reject(res) } }), fail: (res => { if (this._errorHandler != null) { this._errorHandler(res) } reject(res) }) }) }) } } export default request2. 新建一个agriknow类 在agriknow里面做了以下几件事:实现所有业务服务调用,如查询所有新闻列表【getNews】,查询所有课程列表【getCourseList】;实现统一的异常处理,并传给request;将服务端返回的结果response转成response.data回传给API调用的地方;/** * name: agriknow.js * description: 农知汇服务器提供的服务 * author: 徐磊 * date: 2018-5-19 */import request from './request.js'class agriknow { constructor() { this._baseUrl = 'https://apis.xxx.xxx.com/dev/apis/train/v1/' this._defaultHeader = { 'data-tupe': 'application/json' } this._request = new request this._request.setErrorHandler(this.errorHander) } /** * 统一的异常处理方法 */ errorHander(res) { console.error(res) } /** * 查询所有新闻列表 */ getNews(page = 1, size = 10) { let data = { page: page, size: size } return this._request.getRequest(this._baseUrl + 'news/client', data).then(res => res.data) } /** * 获取所有课程 */ getCourseList(page = 1, size = 10, key = null) { let data = key != null ? { page: page, size: size, queryValue: key } : { page: page, size: size } return this._request.getRequest(this._baseUrl + '/course/mobile', data).then(res => res.data) } } export default agriknow3. 函数的调用在app中引用argriknowimport agriknow from './apis/agriknow.js'App({ onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) …… ……定义一个类型为agriknow的属性并实例化import agriknow from './apis/agriknow.js'App({ onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) …… …… }, agriknow:new agriknow() })在Page里调用const app = getApp(); Page({ data: { courseData: [], page: 1, size: 10, total: 0 }, onLoad: function () { …… …… wx.startPullDownRefresh() this.getdataList(); }, //查询课程列表 getdataList() { app.agriknow.getCourseList(this.data.page++, this.data.size, '') .then(res => { wx.stopPullDownRefresh() let list = this.data.page > 2 ? this.data.courseData.concat(res.list) : res.list this.setData({ courseData: list }) }) .catch(res => { wx.stopPullDownRefresh() wx.showToast({ title: '出错了!', icon: 'none' }) }) }, //下拉刷新 onPullDownRefresh() { console.log("下拉刷新"); this.getdataList(); }, …… …… })关于“微信小程序中wx.request如何实现封装”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。...
今天就跟大家聊聊有关C语言中怎么定义一个常量,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。通过在C语言里面定义常量,可以避免代码出现魔数。#include <stdio.h>int main(){ const int AMOUNT = 100; int price = 0; printf("please input amount of money:"); scanf("%d",&price); int change = AMOUNT - price; printf("give you back %d yuan.\n",change); return 0; }定义double类型变量#include <stdio.h>int main(){ printf("height in foot and inch please :"); double foot; double inch; scanf("%lf %lf",&foot,&inch); printf("height is:%f meters\n",((foot + inch / 12)* 0.3048)); return 0;// printf("%f\n",10.0/3*3);// return 0;// }看完上述内容,你们对C语言中怎么定义一个常量有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注辰讯云资讯频道,感谢大家的支持。...
Android中怎么使用ViewPager实现左右循环滑动效果,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。循环滑动效果的实现:PagerAdapter我们知道ViewPager自带的滑动效果非常出色,因此我们基本不需要处理这个滑动,只处理内容的显示。而内容的显示是由Adapter控制的,因此这里重点就是这个Adapter了。为简单起见,本例的每个View直接是一张图片。下面是Adapter的代码:private class ImageAdapter extends PagerAdapter{ private ArrayList<ImageView> viewlist; public ImageAdapter(ArrayList<ImageView> viewlist) { this.viewlist = viewlist; } @Override public int getCount() { //设置成最大,使用户看不到边界 return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0==arg1; } @Override public void destroyItem(ViewGroup container, int position, Object object) { //Warning:不要在这里调用removeView } @Override public Object instantiateItem(ViewGroup container, int position) { //对ViewPager页号求模取出View列表中要显示的项 position %= viewlist.size(); if (position<0){ position = viewlist.size()+position; } ImageView view = viewlist.get(position); //如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。 ViewParent vp =view.getParent(); if (vp!=null){ ViewGroup parent = (ViewGroup)vp; parent.removeView(view); } container.addView(view); //add listeners here if necessary return view; } }这里有几个地方需要注意:getCount() 方法的返回值:这个值直接关系到ViewPager的“边界”,因此当我们把它设置为Integer.MAX_VALUE之后,用户基本就看不到这个边界了(估计滑到这里的时候电池已经挂了吧o_O)。当然,通常情况下设置为100倍实际内容个数也是可以的,之前看的某个实现就是这么干的。instantiateItem() 方法position的处理:由于我们设置了count为 Integer.MAX_VALUE,因此这个position的取值范围很大很大,但我们实际要显示的内容肯定没这么多(往往只有几项),所以这里肯定会有求模操作。但是,简单的求模会出现问题:考虑用户向左滑的情形,则position可能会出现负值。所以我们需要对负值再处理一次,使其落在正确的区间内。instantiateItem() 方法父组件的处理:通常我们会直接addView,但这里如果直接这样写,则会抛出IllegalStateException。假设一共有三个view,则当用户滑到第四个的时候就会触发这个异常,原因是我们试图把一个有父组件的View添加到另一个组件。但是,如果直接写成下面这样:(ViewGroup)view.getParent().removeView(view);则又会因为一开始的时候组件并没有父组件而抛出NullPointerException。因此,需要进行一次判断。也就是上面的代码。destroyItem() 方法:由于我们在instantiateItem()方法中已经处理了remove的逻辑,因此这里并不需要处理。实际上,实验表明这里如果加上了remove的调用,则会出现ViewPager的内容为空的情况。轮播效果的实现:使用Handler进行更新这里我定义了一个Handler来处理ViewPager的轮播。所谓的“轮播”效果实现起来是这样的:每隔一定时间(这里是3秒)切换一次显示的页面。通过控制各页面以一定顺序循环播放,就达到了轮播的效果。为此,我们可以使用Handler的sendEmptyMessageDelayed()方法来实现定时更新,并注意用户也可能会对带有轮播效果的ViewPager手动进行滑动操作,因此我认为用户这时候是希望查看指定页面的,这时候应该取消轮播。下面是这个Handler的实现:private static class ImageHandler extends Handler{ /** * 请求更新显示的View。 */ protected static final int MSG_UPDATE_IMAGE = 1; /** * 请求暂停轮播。 */ protected static final int MSG_KEEP_SILENT = 2; /** * 请求恢复轮播。 */ protected static final int MSG_BREAK_SILENT = 3; /** * 记录最新的页号,当用户手动滑动时需要记录新页号,否则会使轮播的页面出错。 * 例如当前如果在第一页,本来准备播放的是第二页,而这时候用户滑动到了末页, * 则应该播放的是第一页,如果继续按照原来的第二页播放,则逻辑上有问题。 */ protected static final int MSG_PAGE_CHANGED = 4; //轮播间隔时间 protected static final long MSG_DELAY = 3000; //使用弱引用避免Handler泄露.这里的泛型参数可以不是Activity,也可以是Fragment等 private WeakReference<MainActivity> weakReference; private int currentItem = 0; protected ImageHandler(WeakReference<MainActivity> wk){ weakReference = wk; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.d(LOG_TAG, "receive message " + msg.what); MainActivity activity = weakReference.get(); if (activity==null){ //Activity已经回收,无需再处理UI了 return ; } //检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。 if (activity.handler.hasMessages(MSG_UPDATE_IMAGE)){ activity.handler.removeMessages(MSG_UPDATE_IMAGE); } switch (msg.what) { case MSG_UPDATE_IMAGE: currentItem++; activity.viewPager.setCurrentItem(currentItem); //准备下次播放 activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY); break; case MSG_KEEP_SILENT: //只要不发送消息就暂停了 break; case MSG_BREAK_SILENT: activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY); break; case MSG_PAGE_CHANGED: //记录当前的页号,避免播放的时候页面显示不正确。 currentItem = msg.arg1; break; default: break; } } }集成代码:MainActivity下面是MainActivity的代码,主要是加载View和对ViewPager进行初始化设置。因为代码量比较少,重要的部分已经加了注释,就不赘述了public class MainActivity extends Activity { private static final String LOG_TAG = "MainActivity"; private ImageHandler handler = new ImageHandler(new WeakReference<MainActivity>(this)); private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化iewPager的内容 viewPager = (ViewPager) findViewById(R.id.main_viewpager); LayoutInflater inflater = LayoutInflater.from(this); ImageView view1 = (ImageView) inflater.inflate(R.layout.item, null); ImageView view2 = (ImageView) inflater.inflate(R.layout.item, null); ImageView view3 = (ImageView) inflater.inflate(R.layout.item, null); view1.setImageResource(R.drawable.ics); view2.setImageResource(R.drawable.jellybean); view3.setImageResource(R.drawable.kitkat); ArrayList<ImageView> views = new ArrayList<ImageView>(); views.add(view1); views.add(view2); views.add(view3); viewPager.setAdapter(new ImageAdapter(views)); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { //配合Adapter的currentItem字段进行设置。 @Override public void onPageSelected(int arg0) { handler.sendMessage(Message.obtain(handler, ImageHandler.MSG_PAGE_CHANGED, arg0, 0)); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } //覆写该方法实现轮播效果的暂停和恢复 @Override public void onPageScrollStateChanged(int arg0) { switch (arg0) { case ViewPager.SCROLL_STATE_DRAGGING: handler.sendEmptyMessage(ImageHandler.MSG_KEEP_SILENT); break; case ViewPager.SCROLL_STATE_IDLE: handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY); break; default: break; } } }); viewPager.setCurrentItem(Integer.MAX_VALUE/2);//默认在中间,使用户看不到边界 //开始轮播效果 handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY); }//end of onCreate}//end of MainActivity关于Android中怎么使用ViewPager实现左右循环滑动效果问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注辰讯云资讯频道了解更多相关知识。...
Android 中怎么动态加载 jar 文件,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1.1 首先需要了解一点:在Android中可以动态加载,但无法像Java中那样方便动态加载jar 原因:Android的虚拟机(Dalvik VM)是不认识Java打出jar的byte code,需要通过dx工具来优化转换成Dalvik byte code才行。这一点在咱们Android项目打包的apk中可以看出:引入其他Jar的内容都被打包进了classes.dex。 所以这条路不通,请大家注意。1.2 当前哪些API可用于动态加载 1.2.1 DexClassLoader 这个可以加载jar/apk/dex,也可以从SD卡中加载,也是本文的重点。 1.2.2 PathClassLoader 只能加载已经安装到Android系统中的apk文件。1.3 代码 package com.example.dynamicloaddemo.jar; public class DynamicTest implements IDynamic { @Override public String helloWorld() { return "Hello World Form JAR"; } } 将这个类导出,注意,编译的时候必须是 java 1.6 编译(java 1.7 编译会出现错误:Zip is good, but no classes.dex inside, and no valid .odex file in the same directory)1.4 编译文件之后将上面的类打包为 dynamic.jar 1.4.1 下载jar 转化 dex 工具,将打包好的jar拷贝到SDK安装目录android-sdk-windows\platform-tools下,DOS进入这个目录,执行命名: dx --dex --output=test.jar dynamic.jar 1.4.2 将test.jar拷贝到 /data/data/packagename/app-libs/ 放在 SDCard 上会出现错误 Optimized data directory /data/data/com.example.dynamicloaddemo/files/test.jar is not owned by the current user. Shared storage cannot protect your application from code injection attacks.程序代码:public class MainActivity extends Activity { Button mToastButton;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mToastButton = (Button) findViewById(R.id.main_btn);// Before the secondary dex file can be processed by the DexClassLoader,// it has to be first copied from asset resource to a storage location.// final File dexInternalStoragePath = new File(getDir("dex",// Context.MODE_PRIVATE),SECONDARY_DEX_NAME);// if (!dexInternalStoragePath.exists()) {// mProgressDialog = ProgressDialog.show(this,// getResources().getString(R.string.diag_title),// getResources().getString(R.string.diag_message), true, false);// // Perform the file copying in an AsyncTask.// // 从网络下载需要的dex文件// (new PrepareDexTask()).execute(dexInternalStoragePath);// } else {// mToastButton.setEnabled(true);// }System.out.println(getDir("libs", Context.MODE_PRIVATE)); System.out.println(getFilesDir()); System.out.println(getCacheDir()); System.out.println(getDir("libs", Context.MODE_PRIVATE)); System.out.println(getDir("libs", Context.MODE_PRIVATE)); mToastButton.setOnClickListener(new View.OnClickListener() {public void onClick(View view) {// Internal storage where the DexClassLoader writes the// optimized dex file to.// final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);final File optimizedDexOutputPath = new File(getDir("libs", Context.MODE_PRIVATE) + File.separator + "test.jar");// Initialize the class loader with the secondary dex file.// DexClassLoader cl = new// DexClassLoader(dexInternalStoragePath.getAbsolutePath(),// optimizedDexOutputPath.getAbsolutePath(),// null,// getClassLoader());/* DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(), Environment .getExternalStorageDirectory().toString(), null, getClassLoader()); */DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(), getDir("libs", Context.MODE_PRIVATE).getAbsolutePath(), null, getClassLoader()); Class<?> libProviderClazz = null;try {// Load the library class from the class loader.// 载入从网络上下载的类// libProviderClazz =// cl.loadClass("com.example.dex.lib.LibraryProvider");libProviderClazz = cl.loadClass("com.example.dynamicloaddemo.jar.DynamicTest");// Cast the return object to the library interface so that// the// caller can directly invoke methods in the interface.// Alternatively, the caller can invoke methods through// reflection,// which is more verbose and slow.// LibraryInterface lib = (LibraryInterface)// libProviderClazz.newInstance();IDynamic lib = (IDynamic) libProviderClazz.newInstance();// Display the toast!// lib.showAwesomeToast(view.getContext(), "hello 世界!");Toast.makeText(MainActivity.this, lib.helloWorld(), Toast.LENGTH_SHORT).show(); } catch (Exception exception) {// Handle exception gracefully here.exception.printStackTrace(); } } }); } }1.5 运行,出现 Hello World From JAR , 表明动态加载成功。 看完上述内容,你们掌握Android 中怎么动态加载 jar 文件的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注辰讯云资讯频道,感谢各位的阅读!...
本篇文章给大家分享的是有关android 中怎么提高App的启动速度,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。先记录下提速的方法application的Oncreate方法里面的逻辑代码全用new Handler().post(new Runnable());包裹Activity的Oncreate()方法如此;这种方法会把代码执行的时序全拖到onResumer之后3,然后在Oncreate onresume 方法 一切你认为会阻碍到启动速度的方法和代码加上log打印信息看图看点击的activitymanager 他会打印时间,只要看下打印时间之前的log打印了哪些信息然后判断这些打印信息的代码是否会阻碍到app的启动速度如果会的话就把这些代码全部new Handler.post掉,或者new Thread 掉。但是顺序也会有先后,打印下log慢慢体会。我的app在启动的时候会扫描数据库,但是newHandler new Thread 后发现还是在 启动时间之前就已经运行了,这证明启动的时候会去扫描数据库,时间慢了100ms。于是用new Handler.postDaley();延长90ms运行,发现可以把这个扫描的模块拖到打印时间之后运行,app启动后视图出来了才开始扫描数据库,速度又提高了100ms达到了800ms左右。4. 另外还要注意布局的深度,尽量简化布局。以上就是android 中怎么提高App的启动速度,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注辰讯云资讯频道。...
这篇文章将为大家详细讲解有关Android中怎么自定义弹出对话框,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。1、首先新建一个xml文件,这里以设置音效开关为例myview.xml如下:<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/toggleButton1" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="@string/voice" android:textSize="20sp" /> <ToggleButton android:id="@+id/toggleButton1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_toRightOf="@+id/textView1" android:text="ToggleButton" android:textOff="OFF" android:textOn="ON" /></RelativeLayout>2、在代码中调用自定义视图public void setSound(){// 取得自定义View LayoutInflater layoutInflater = LayoutInflater.from(MainActivity.instance); //MainActivity.instance是在MainActivity.java中定义的,public static MainActivity instance;View myLoginView = layoutInflater.inflate(R.layout.myview, null); myToggleButton = (ToggleButton)myLoginView.findViewById(R.id.toggleButton1);if(audio_on){ myToggleButton.setChecked(true); }else{ myToggleButton.setChecked(false); } Dialog alertDialog = new AlertDialog.Builder(MainActivity.instance) .setTitle("设置") .setView(myLoginView) .setIcon(android.R.drawable.ic_dialog_info) .setPositiveButton("确定",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// TODO Auto-generated method stubif(myToggleButton.isChecked()){ audio_on = true; }else{ audio_on = false; } } }). create(); alertDialog.show(); }关于Android中怎么自定义弹出对话框就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。...
这篇文章主要介绍“怎么搭建本地K8s开发环境”,在日常操作中,相信很多人在怎么搭建本地K8s开发环境问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么搭建本地K8s开发环境”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!一、依赖检查开始之前需要先检查一下1.是否已经安装好了 Docker 环境如果还没安装 Mac/Windows 的用户可以直接安装 Docker DesktopWindows 用户推荐使用 WSL22.是否已经安装好了 Golang 环境, Go 版本最好 >= 1.15如果没有可以参考官方文档进行安装二、使用 Kind 搭建本地开发环境2.1 安装如果本地存在 Golang 环境可以直接执行下方命令进行安装GO111MODULE="on" go get sigs.k8s.io/kind@v0.10.0 && kind create cluster如果不想通过源码安装可以查看官方文档直接安装编译好的二进制文件执行下方命令可以输出 kind 的版本就表示安装好了❯ kind version kind v0.10.0 go1.16 linux/amd642.2 创建一个 K8s 集群使用下方命令即可创建一个简单的单节点 K8s 集群node create clutser集群的创建时间和你的网络环境以及机器的性能有关系,kind 会去 docker hub 上拉取 kindest/node 镜像,这个镜像大概有 400M 的大小,当出现下方的提示的时候就说明集群创建好了❯ kind create cluster Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.20.2) ? ✓ Preparing nodes ? ✓ Writing configuration ? ✓ Starting control-plane ?️ ✓ Installing CNI ? ✓ Installing StorageClass ? Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Thanks for using kind! ?我们可以使用 kind get clusters 获取我们创建的集群列表,kind 支持创建多个集群,不手动指定集群名称的时候默认名为 kind ,我们也可以像下面一样在创建集群的时候使用 --name 指定集群名称kind create cluster --name mohuishou可以看到我们两个集群都创建好了❯ kind get clusters kind mohuishou现在我们只需要一个集群,所以可以先使用 kind delete clusters mohuishou 将刚刚创建的集群先删除掉2.3 使用集群下面我们来看一下 kubectl 的常用命令是否都可以正常使用,并且部署一个简单的 web 服务试试查看集群信息❯ kubectl cluster-info --context kind-kind Kubernetes master is running at https://127.0.0.1:41801 KubeDNS is running at https://127.0.0.1:41801/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.可以看到我们 k8s master 地址和 dns 的地址,注意一般情况下我们只创建一个集群不需要指定 --context cluster name 但是创建了多个集群时这个基本就是必须的一个命令了。如果不加这个参数可能会报下面的错误The connection to the server localhost:8080 was refused - did you specify the right host or port?这是因为 kubectl默认连接的 apiserver 地址是 localhost:8080但是我们的 apiserver 地址不是这个所以报错。为什么我们使用 --context cluster-name 就可以了呢?这是因为 kind 在创建集群的时候会修改 $HOME/.kube/config 的配置,将集群的 apiserver 地址,证书等相关信息都自动写入进去了每次命令都要加上这个参数好麻烦怎么办?我们可以使用 kubectl config use-context context-name 来进行设置,设置好了之后我们后续就不用每次都加上 –context 的参数了,同时还可以通过 kubectl config current-context 查询我们当前默认操作的集群是哪一个查看集群节点列表可以发现我们部署的是一个单节点的 v1.20.2 的集群❯ kubectl get no NAME STATUS ROLES AGE VERSION kind-control-plane Ready control-plane,master 20m v1.20.2部署一个 Nginx 服务使用下方代码创建一个 nginx.yml 文件,然后使用 kubectl apply -f nginx.yml 就可以完成部署了apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80查看当前 deployment 的状态❯ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 0/3 3 0 39s查看 pod 的状态❯ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-deployment-66b6c48dd5-2s5cb 1/1 Running 0 84s nginx-deployment-66b6c48dd5-8wf8b 1/1 Running 0 84s nginx-deployment-66b6c48dd5-zc6vd 1/1 Running 0 84s由于我们没有做服务暴露,所以是不能直接访问对应的服务的,我们可以用 kubectl提供的端口转发功能来讲流量从本地转发给 k8s 集群❯ kubectl port-forward nginx-deployment-66b6c48dd5-2s5cb 30080:80 Forwarding from 127.0.0.1:30080 -> 80 Forwarding from [::1]:30080 -> 80 Handling connection for 30080我们访问 http://localhost:30080 就会发现这个熟悉的 nginx 界面image-20210424210804343到这里可以发现我们集群已经是可以使用了2.4 进阶使用2.4.1 创建一个多节点的集群kind 默认创建的是一个单节点的集群,我们可以通过修改配置创建一个高可用的集群,我们创建一个 3 个 master 节点,两个 worker 节点的集群kind create cluster --name mohuishou-ha --config kind-ha.yml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: control-plane - role: control-plane - role: worker - role: worker看一下节点即可验证❯ kubectl --context kind-mohuishou-ha get no NAME STATUS ROLES AGE VERSION mohuishou-ha-control-plane Ready control-plane,master 16m v1.20.2 mohuishou-ha-control-plane2 Ready control-plane,master 14m v1.20.2 mohuishou-ha-control-plane3 Ready control-plane,master 13m v1.20.2 mohuishou-ha-worker Ready <none> 5m52s v1.20.2 mohuishou-ha-worker2 Ready <none> 5m52s v1.20.22.4.2 Load Image一般来说我们在 k8s 内部署应用需要先把容器镜像推送到到镜像仓库当中,这样在本地开发的时候相对来说会比较麻烦,特别是镜像比较大的时候,往返会有两次网络消耗,为了解决这个问题我们可以使用 kind load 镜像的功能直接把镜像。kind load docker-image my-custom-image-0 my-custom-image-1 --name kind排坑指南:load image 成功,但是部署 pod 报错Failed to pull image "controller:latest": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/library/controller:latest": failed to resolve reference "docker.io/library/controller:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed这个问题之前卡了我很久,还是有点坑1.进入节点查看: 镜像是否存在获取节点名▶ kubectl get nodes NAME STATUS ROLES AGE VERSION kind-control-plane Ready control-plane,master 2d1h v1.20.2进入终端docker exec -it kind-control-plane bash查看镜像是否存在,kind 创建的集群使用的是 containerd 所以我们使用 crictl 命令来获取crictl img | grep controller docker.io/library/controller latest 421cbf77618ba 72.1MB2.如果镜像存在,也就是我们上面看到的情况,这时候就要检查一下我们部署的 yaml 文件kubectl -n node-pool-operator-system get deployment -o yaml | grep imagePullPolicy是否存在: imagePullPolicy: Always 如果我们没有改 yaml 的话默认会是这个配置,这个配置会导致每次都去镜像仓库拉取镜像,改成 imagePullPolicy: IfNotPresent 就可以了3.如果镜像不存在:这时候要检查一下有没有指定 cluster-name,在存在多个集群的情况下可能没有加载到我们想要的集群,加上 --name cluster-name 即可总结kind 在创建集群的时候实际上使用的是 kubeadm 所以还可以修改 kubeadm 的配置来修改默认的镜像地址,节点标签污点等信息,除此之外还可以配置 PV/PVC CNI 插件等配置,如果有需求的话可以查阅 kind 的官方文档不过要注意的是 kind 不支持给运行的集群添加节点,如果需要多节点集群的话得提前规划好节点数量到此,关于“怎么搭建本地K8s开发环境”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注辰讯云网站,小编会继续努力为大家带来更多实用的文章!...
android 中怎么监听SD卡文件变化,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。(1)创建目录监听器: import android.os.FileObserver; import android.util.Log; /** * SD卡中的目录创建监听器。 * * @author mayingcai */ public class SDCardListener extends FileObserver { public SDCardListener(String path) { /* * 这种构造方法是默认监听所有事件的,如果使用 super(String,int)这种构造方法, * 则int参数是要监听的事件类型. */ super(path); } @Override public void onEvent(int event, String path) { switch(event) { case FileObserver.ALL_EVENTS: Log.d("all", "path:"+ path); break; case FileObserver.CREATE: Log.d("Create", "path:"+ path); break; } } }(2)给目录设置监听器: SDCardListener listener = new SDCardListener("目录"); //开始监听 listener.startWatching(); /* * 在这里做一些操作,比如创建目录什么的 */ //停止监听 listener.stopWatching();注意事项:FileObserver对象必须保持一个引用,确保不被垃圾收集器回收掉,否则就不会触发事件,这里可以考虑使用Service服务。2 如果要在onEvent中做较多操作,最好使用线程去做 ,以免因为阻塞接收不到后面的事件。3.风信子经过测试,FileObserver所监听的某一文件夹下的子文件夹中的文件信息被改变时,提供的path只是该子文件夹的path,并不是具体文件的path,所以要监听所有文件的操作时,最好是给每一个文件夹都设置FileObserver监听,可以具体到每一个文件的修改,否则某些应用对文件进行修改后会监听不到,例如已监听SDCARD文件目录,当Gallery对/sdcard/xxx进行修改等操作时,会监听到,当Gallery对/sdcard/demo/xxxx进行操作时不会被监听到,最好是对/sdcard以及/sdcard/demo连个文目录分别设置FileObserver监听就可以解决这个问题。注:此方法只在SDCard有20个左右的文件夹时测试,如果文件夹过多会不会影响太多的效率并不清楚。我发现Dbank也是监听了固定的几个含有图片的文件夹ps:下面介绍监听SD卡状态的三部曲第一步,创建一个广播接收者, SDReceiver extends BroadcastReceiver ;第二步,在AndroidManifest.xml配置文件中注册广播接收者与配置监SD卡状态发生变化时对应的Action: <receiver android:name="com.itheima.sdlistener.SDReceiver"> <intent-filter > <action android:name="android.intent.action.MEDIA_MOUNTED"/> <action android:name="android.intent.action.MEDIA_REMOVED"/> <action android:name="android.intent.action.MEDIA_UNMOUNTED"/> <data android:scheme="file"/> </intent-filter> </receiver>第三步,重写广播接受者中OnReceiver方法:public void onReceive(Context context, Intent intent) { //判断收到的是神马广播//获取广播中的actionString action = intent.getAction();if(Intent.ACTION_MEDIA_MOUNTED.equals(action)){ Toast.makeText(context, "sd卡就绪", 0).show(); }else if(Intent.ACTION_MEDIA_REMOVED.equals(action)){ Toast.makeText(context, "sd卡被拔出了", 0).show(); }else if(Intent.ACTION_MEDIA_UNMOUNTED.equals(action)){ Toast.makeText(context, "sd卡被卸载了", 0).show(); } }关于android 中怎么监听SD卡文件变化问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注辰讯云资讯频道了解更多相关知识。...
今天就跟大家聊聊有关android中Context有什么用,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1、Context认知。Context译为场景,一个应用程序可以认为是一个工作环境,在这个工作环境中可以存在许多场景,coding代码的场景 ,打电话的场景,开会的场景。这些场景可以类比不同的Activity,service。2、从两个角度认识Context。第一:Activity继承自Context,同时Activity还实现了其他的interface,我们可以这样看,activity在语法上extends了Context,其本质上是一个Context,但同时其实现了许多interface,扩充了Context的功能,扩充之后的类成为Activity或者Service。第二:Context本质上包含了场景的所有元素,故而设定其为abstract,Activity和Service继承自Context,它们本质上可以认为就是Context。3、Context继承关系图4、Application对象的ContextImpl对象创建过程。step 1、Ams通过远程Binder调用ActivityThread的内部类ApplicationThread的bingApplication方法,参数包括ApplicationInfo,这个对象由Ams创建,通过IPC传递到ActivityThread的内部类ApplicationThread中。public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, int debugMode, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) { if (services != null) { // Setup the service cache in the ServiceManager ServiceManager.initServiceCache(services); } setCoreSettings(coreSettings); AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; data.providers = providers; data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; data.debugMode = debugMode; data.restrictedBackupMode = isRestrictedBackupMode; data.persistent = persistent; data.config = config; data.compatInfo = compatInfo; data.initProfileFile = profileFile; data.initProfileFd = profileFd; data.initAutoStopProfiler = false; queueOrSendMessage(H.BIND_APPLICATION, data); }step 2、构建AppBindData对象,如上代码所示。step 3、调用H Handler,执行handleBindApplication()方法。static final class AppBindData { LoadedApk info; String processName; ApplicationInfo appInfo; List<ProviderInfo> providers; ComponentName instrumentationName; Bundle instrumentationArgs; IInstrumentationWatcher instrumentationWatcher; int debugMode; boolean restrictedBackupMode; boolean persistent; Configuration config; CompatibilityInfo compatInfo; /** Initial values for {@link Profiler}. */ String initProfileFile; ParcelFileDescriptor initProfileFd; boolean initAutoStopProfiler; public String toString() { return "AppBindData{appInfo=" + appInfo + "}"; } } private void handleBindApplication(AppBindData data) { mBoundApplication = data; mConfiguration = new Configuration(data.config); mCompatConfiguration = new Configuration(data.config); //.......... TimeZone.setDefault(null); /* * Initialize the default locale in this process for the reasons we set the time zone. */ Locale.setDefault(data.config.locale); data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//data.info对象为LoadApk,此时data.info为null,使用getPackageINfoNoCheck创建此对象。 if (data.instrumentationName != null) {//该条件尽在Android Unit Test工程时会执行到,此处直接看else语句 ContextImpl appContext = new ContextImpl(); appContext.init(data.info, null, this); InstrumentationInfo ii = null; try { ii = appContext.getPackageManager(). getInstrumentationInfo(data.instrumentationName, 0); } catch (PackageManager.NameNotFoundException e) { } if (ii == null) { throw new RuntimeException( "Unable to find instrumentation info for: " + data.instrumentationName); } mInstrumentationAppDir = ii.sourceDir; mInstrumentationAppPackage = ii.packageName; mInstrumentedAppDir = data.info.getAppDir(); ApplicationInfo instrApp = new ApplicationInfo(); instrApp.packageName = ii.packageName; instrApp.sourceDir = ii.sourceDir; instrApp.publicSourceDir = ii.publicSourceDir; instrApp.dataDir = ii.dataDir; instrApp.nativeLibraryDir = ii.nativeLibraryDir; LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, appContext.getClassLoader(), false, true); ContextImpl instrContext = new ContextImpl(); instrContext.init(pi, null, this); try { java.lang.ClassLoader cl = instrContext.getClassLoader(); mInstrumentation = (Instrumentation) cl.loadClass(data.instrumentationName.getClassName()).newInstance(); } catch (Exception e) { throw new RuntimeException( "Unable to instantiate instrumentation " + data.instrumentationName + ": " + e.toString(), e); } mInstrumentation.init(this, instrContext, appContext, new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher); if (mProfiler.profileFile != null && !ii.handleProfiling && mProfiler.profileFd == null) { mProfiler.handlingProfiling = true; File file = new File(mProfiler.profileFile); file.getParentFile().mkdirs(); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); } try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ": " + e.toString(), e); } } else { mInstrumentation = new Instrumentation();//初始化Instrumentation对象,一个应用程序对应一个Instrumentation对象 } Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; try { mInstrumentation.callApplicationOnCreate(app);//调用Application程序都应的onCreate方法。 } catch (Exception e) { if (!mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } }第三步可以又可以分为三小步。step 3.1、给AppBindData的info变量赋值。data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//data.info对象为LoadApk,此时data.info为null,使用getPackageINfoNoCheck创建此对象。step 3.2、初始化Instrumentation对象。mInstrumentation = new Instrumentation();//初始化Instrumentation对象,一个应用程序对应一个Instrumentation对象step 3.3、创建Application对象。Application app = data.info.makeApplication(data.restrictedBackupMode, null);我们着重看一下step 3.1和step3.3.step 3.1:mPackages和mResourcePackages集合,以packageName为key值,我们知道一个应用程序中的packageName是相同的,也就是说,此处一旦创建,其他地方再次调用此函数,就不需要创建了。总结:也就是说一个应用程序中的LoadedApk对象是唯一的。此处的LoadedApk,也被称为packageInfo。public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { return getPackageInfo(ai, compatInfo, null, false, true); } private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {/*includeCode 默认为true*/ synchronized (mPackages) { WeakReference<LoadedApk> ref; if (includeCode) {//1、首先从mPackages或者mResourcePackages 集合中以packageName为Key值,获取LoadApk对象。 ref = mPackages.get(aInfo.packageName); } else { ref = mResourcePackages.get(aInfo.packageName); } LoadedApk packageInfo = ref != null ? ref.get() : null; if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null ? mBoundApplication.processName : null) + ")"); packageInfo = new LoadedApk(this, aInfo, compatInfo, this, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);//2、如果packageInfo对象为null,则new初始化此对象 if (includeCode) {//3、最后将创建的此packageInfo对象,加入到mPackages或者mResourcePackages集合中。 mPackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } else { mResourcePackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } } return packageInfo; } }step 3.3、总结:每个应用程序都存在一个Application,用户可以在AndroidManifest中重写它,如果不重写也存在一个默认的Application对象。framework/base/core/java/android/app/LoadedApk.javapublic Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { return mApplication; } Application app = null; String appClass = mApplicationInfo.className; if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application";//1、每个工程都存在一个Application对象,默认的Application对象为android.app.Application,客户端可以重写 } try { java.lang.ClassLoader cl = getClassLoader(); ContextImpl appContext = new ContextImpl();//2、创建ContextImpl对象,这才是Context的实际实现类 appContext.init(this, null, mActivityThread);//3、执行ContextImpl对象的init方法,initResource等对象 app = mActivityThread.mInstrumentation.newApplication(//4、以appContext为参数得到Application对象。 cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { if (!mActivityThread.mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to instantiate application " + appClass + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app);//5、将创建的Application对象,加入到A来了Application中。 mApplication = app; if (instrumentation != null) {//6、此时的instrumentation为null。 try { instrumentation.callApplicationOnCreate(app); } catch (Exception e) { if (!instrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } } return app; }5、Activity中Context的创建过程step 1、Ams通过远程Binder调用ActivityThread的Application的scheduleLaunchActivity方法,参数包括ActivityInfo,这个对象由Ams创建,通过IPC传递到ActivityThread的内部类ApplicationThread中。public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) { ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; r.profileFile = profileName; r.profileFd = profileFd; r.autoStopProfiler = autoStopProfiler; updatePendingConfiguration(curConfig); queueOrSendMessage(H.LAUNCH_ACTIVITY, r); }step 2、构建ActivityClientRecord对象,如上代码所示。step 3、调用H Handler,执行handleLaunchActivity()方法。其中step 3,又可分为10小步。private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) {//1、如果packageInfo为null,则调用getPackageInfo的得到LoadedApk r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component); } if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } Activity activity = null; try {//2、调用mInstrumentation的newActivity方法,得到Activity对象 java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { Application app = r.packageInfo.makeApplication(false, mInstrumentation);//3、获取Application对象 if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); if (activity != null) {//4、创建ContextImpl对象 ContextImpl appContext = new ContextImpl(); appContext.init(r.packageInfo, r.token, this); appContext.setOuterContext(activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config);//5、执行Activity的attach方法,将此ContextImpl对象,设置给Activity,activity会调用attachBaseContext if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource();//6、设置主题 if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false; mInstrumentation.callActivityOnCreate(activity, r.state);//7、执行Activity的onCreate方法 if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; if (!r.activity.mFinished) { activity.performStart();//8、执行Activity的onStart方法 r.stopped = false; } if (!r.activity.mFinished) { if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);//9、质细腻感onRestoresInstanceState方法 } } if (!r.activity.mFinished) { activity.mCalled = false; mInstrumentation.callActivityOnPostCreate(activity, r.state); if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()"); } } } r.paused = true; mActivities.put(r.token, r);//10、将包含activity信息集的r对象,也就是ActivityClientRecord,加入到mActivities中,r.token为key值。 } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }总结:activity的packageInfo对象和application的packageInfo是同一个对象。6、Service中Context的创建过程step 1、Ams通过远程Binder调用ActivityThread的内部类ApplicationThread的scheduleCreateService方法,参数包括serviceInfo,这个对象由Ams创建,通过IPC传递到ActivityThread的内部类ApplicationThread中。public final void scheduleCreateService(IBinder token, ServiceInfo info, CompatibilityInfo compatInfo) { CreateServiceData s = new CreateServiceData(); s.token = token; s.info = info; s.compatInfo = compatInfo; queueOrSendMessage(H.CREATE_SERVICE, s); }step 2、构建CreateServiceData对象,如上代码所示。step 3、调用H Handler,执行handleCreateService()方法。其中step 3又可分为一下5步。private void handleCreateService(CreateServiceData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo);//1、得到packageInfo,调用getPackageInfoNoCheck Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = (Service) cl.loadClass(data.info.name).newInstance(); } catch (Exception e) { if (!mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to instantiate service " + data.info.name + ": " + e.toString(), e); } } try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); ContextImpl context = new ContextImpl();//2、创建ContextImpl对象 context.init(packageInfo, null, this); Application app = packageInfo.makeApplication(false, mInstrumentation);//3、得到Application对象 context.setOuterContext(service); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());//4、调用service的attach方法,将实例化的ContextImpl设置给Service service.onCreate(); mServices.put(data.token, service);//5、将service对象加入到mService集合中,key值为data.token。 try { ActivityManagerNative.getDefault().serviceDoneExecuting( data.token, 0, 0, 0); } catch (RemoteException e) { // nothing to do. } } catch (Exception e) { if (!mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to create service " + data.info.name + ": " + e.toString(), e); } } }综上所述:1、无论是Application还是Activity、Service,他们的LoadedApk对象都是同一个,或者说packageInfo为同一个对象。2、在创建ContextImpl对象时,Application和SErvice通过getPackageInfoNoCheck方法,Activity通过getPackageInfo方法得到。3、一个应用程序中Context的个数 = Activity的数量+Service的数量 +1。这里的1代表Application。4、应用程序中包含着多个ContextImpl对象,其内部的PackageInfo却是同一个。这样设计意味着ContextImpl是一个轻量级类,PackageInfo是一个重量级类,所有和包相关的操作封装到PackageInfo中,有利于代码的封装与隐藏。class ContextImpl extends Context { private final static String TAG = "ApplicationContext"; private final static boolean DEBUG = false; private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = new HashMap<String, SharedPreferencesImpl>(); /*package*/ LoadedApk mPackageInfo;看完上述内容,你们对android中Context有什么用有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注辰讯云资讯频道,感谢大家的支持。...
今天就跟大家聊聊有关android中怎么将图片上传到PHP,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。复制代码 代码如下:[java] view plaincopyprint?//设置超时时间 httpclient.setTimeout(20000); 再building,runing,还是不行。。。。这就怪了,明明好好的,怎么会突然就变成连接超时了呢!又折腾了一阵子后,也跟后台那边的朋友沟通过,他也测试了上传接口,发现没什么问题,就让我自己去折腾去了。。。。我就郁闷了,看不出原先的代码有什么错误,也没什么法子了,就出最下下策吧,自己搭一个PHP上传图片接口,亲自测试下到底是怎么回事。。。。1.首先,你得下一个方便快捷的PHP服务器,我这里用了WampServer,百度----下载-----安装-----启动,浏览器输入:http://127.0.0.1 有页面显示,OK了。就这么简单!2.浏览器输入 : http://本机IP地址 回车, 发现报错,类似“You don't have permission to access / on this server” 说明你的WM还没设置,需要进行如下设置:造成这个问题的原因是Apache 的http.conf内的默认配置是复制代码 代码如下:# onlineoffline tag - don't removeOrder Deny,AllowDeny from allAllow from 127.0.0.1只允许127.0.0.1访问,点击wampserver图标让后点击Putonline,http.conf内的以上默认配置自动修改为复制代码 代码如下:# onlineoffline tag - don't removeOrder Allow,DenyAllow from all现在localhost可以访问了。同样phpMyadmin在localhost下不能正常访问在127.0.0.1能正常访问,解决方法:点击根目录下的alias目录,打开phpmyadmin.conf配置文件,和上面修改http.conf一样把复制代码 代码如下:Deny from allAllow from 127.0.0.1 修改为Allow from all3. 再此输入 : http://本机IP地址 回车 显示页面 OK! 至于为什么要第二步、第三步呢,我就不说了。。。留给新人去想想吧! 大神直接无视。。。。。4.写一个上传图片的PHP文件,当然我一个敲java的孩子一下子怎么可能憋的出来,那怎么办,当然是百度参考别人的了,下面的PHP代码源自网络,亲测没有错误:[php] view plaincopyprint?<?php $base_path = "./upload/"; //存放目录 if(!is_dir($base_path)){ mkdir($base_path,0777,true); } $target_path = $base_path . basename ( $_FILES ['attach'] ['name'] ); if (move_uploaded_file ( $_FILES ['attach'] ['tmp_name'], $target_path )) { $array = array ( "status" => true, "msg" => $_FILES ['attach'] ['name'] ); echo json_encode ( $array ); } else { $array = array ( "status" => false, "msg" => "There was an error uploading the file, please try again!" . $_FILES ['attach'] ['error'] ); echo json_encode ( $array ); } ?>5.将上面的php文件放在WM安装目录下的www目录下,我的如下图所示,仅供参考:6.经过上面几个步骤,PHP端已经搭建好了,现在就是回到android端改改IP地址测试下就oK了,代码段如下:[java] view plaincopyprint? //HTTP上传图片 RequestParams params = new RequestParams(); try { //将压缩后的bitmap保存为图片文件 String saveImgPath=getSD_Path()+"/saveimg.png"; File saveimg=new File(saveImgPath); FileOutputStream fos = new FileOutputStream(saveimg); bmp.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); //上传压缩后的文件,大约100k左右 File uploadImg=new File(saveImgPath); <span >params.put("attach", uploadImg);</span> } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //上传地址 String url=URLConfigs.UploadHeadImage_ukey+myprefs.Ukey().get(); <span >String url="http://192.168.0.8/upload.php";</span> LogUtil.e(TAG, "upload img url :"+url); AsyncHttpUtil.post_loading(context,url, params, new MyTextHttpResponseHandler() { @Override public void onSuccess(int status, Header[] arg1, String json) { super.onSuccess(status, arg1, json); LogUtil.e(TAG, "上传图片 json :"+json); RespondBaseEntity entity=GsonUtil.GetFromJson(json, RespondBaseEntity.class); if(entity.isStatus()){ //上传成功,设置图片 face.setImageBitmap(bmp); ToastUtils.show(context, "上传成功"); }else{ ToastUtils.show(context, json); } myprefs.position().put(0); } @Override public void onFailure(int arg0, Header[] arg1, String arg2, Throwable arg3) { super.onFailure(arg0, arg1, arg2, arg3); myprefs.position().put(0); arg3.printStackTrace(); ToastUtils.show(context, R.string.network_unavailable); }params.put("attach", uploadImg); 这里的attach参数是和服务端一一对应的,别乱改。。。。String url="http://192.168.0.8/upload.php"; 这个192.168.0.8是我的PHP部署的地址,改成你自己的就行了。PS:别犯2,用了127.0.0.1 想想为啥不能用127.0.0.1到此就是building,runing了。 发现OK。。。。 可以上传,并在www目录下找到upload目录,upload目录下有上传的图片。。。。看完上述内容,你们对android中怎么将图片上传到PHP有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注辰讯云资讯频道,感谢大家的支持。...