AsyncTaskLoaderの挙動

AsyncTaskLoaderについてはさんざん悩まされてきたけど、また再びバグって大変だった。

具体的にはActivityから離れて戻ってきた時にonStartLoading→onLoadFinishedが流れるんだけど、何度も何度もActivityに戻るときに同じ処理が流れてて本当に謎でバグの原因だった。

今使っている自作のAsyncLoaderはJSONを元に使っているのだけど、JSONに変換出来ない場合がある。その時はNULLを流すようにしてるんだけどそれがイケなかったようだ。

@Override
public void deliverResult(JSONObject data) {
    if (isReset()) {
        if (this.result != null) {
            //this.result = null;
        }
        return;
    }

    String strerror="{\"" + "this" + "\"" + ":" +"\""+"ReConnect" + "\""+"}";//{"this":"FailConnect"}
    JSONObject aaa=new JSONObject();
    try{aaa=new JSONObject(strerror);}catch (JSONException e2) {}//なんかめんどくさい


    this.result = aaa;

    super.deliverResult(data);
    if (isStarted()) {
    }
}

@Override
protected void onStartLoading() {
    if (this.result != null) {
        deliverResult(this.result);
    }
    if (takeContentChanged() || this.result == null) {
        forceLoad();
    }
}

取り敢えず荒療治でむりやりResultに値をセットして、復帰時の処理を判別できるようにした。

これなら取り敢えずは forceLoad()に行かずにそのままdeliverResultしてくれる。

ゼッタイ何か問題ある気がするけど取り敢えず様子見。

 

取り敢えず今のところの挙動として、

なんか違うURLかつLoaderIDが同じでgetSupportLoaderManager().restartLoaderをすると、最初したURLのLoaderでonLoadFinishedが流れる。

でもなんか同じURLで同じLoaderIDだったらonLoadFinishedは一度しか流れず正常に行く感じ

つまりひとつのURLに1つのLoaderIDを指定すれば大丈夫ってこと???

謎だー

PHPでandroid,iphoneからの画像の回転処理

AndroidやiPhoneでは画像を保存した時縦横のサイズではなくExifのOrientationで画像の向きを管理しているらしい。

なるほど、そのまま縮小しても向きがおかしいはずだ。

よってOrientationを考慮した動作が必要になる。

// コピー元画像の指定
$targetImage = $target_path;
// ファイル名から、画像インスタンスを生成
$image = imagecreatefromjpeg($targetImage);
// コピー元画像のファイルサイズを取得
list($image_w, $image_h) = getimagesize($targetImage);

// 出力する画像サイズの指定
//$width =640;
//$height = $width*$image_h/$image_w;


$min_width = 800; // 幅の最低サイズ
$min_height = 800; // 高さの最低サイズ


if($image_w == $image_h) {
   $width = $min_width;
   $height = $min_height;
} else if($image_w > $image_h) {//横長の場合
   $width = $min_width;
   $height = $image_h*($min_width/$image_w);
} else if($image_w < $image_h) {//縦長の場合
   $width = $image_w*($min_height/$image_h);
   $height = $min_height;
}


// サイズを指定して、背景用画像を生成
$canvas = imagecreatetruecolor($width, $height);

// 背景画像に、画像をコピーする
imagecopyresampled($canvas,  // 背景画像
      $image,   // コピー元画像
      0,        // 背景画像の x 座標
      0,        // 背景画像の y 座標
      0,        // コピー元の x 座標
      0,        // コピー元の y 座標
      $width,   // 背景画像の幅
      $height,  // 背景画像の高さ
      $image_w, // コピー元画像ファイルの幅
      $image_h  // コピー元画像ファイルの高さ
);



$exif_datas = exif_read_data($target_path);
//返り値として連想配列でExif情報が取得できます。

//var_dump($exif_datas);
if(isset($exif_datas["Orientation"])){
 if($exif_datas["Orientation"]==6){

 // 回転
 $canvas = imagerotate($canvas, 270, 0);

 }
 if($exif_datas["Orientation"]==8){

 // 回転
 $canvas = imagerotate($canvas, 90, 0);

 }
 if($exif_datas["Orientation"]==3){

 // 回転
 $canvas = imagerotate($canvas, 180, 0);

 }
 }
// 画像を出力する
imagejpeg($canvas,           // 背景画像
      "./shohou_image_m/".$new_dir."/" . basename( $_FILES['f1']['name'])."_0.jpg",    // 出力するファイル名(省略すると画面に表示する)
      100                // 画像精度(この例だと100%で作成)
);

// メモリを開放する
imagedestroy($canvas);

こんなところかなー

スクリーンのON時にFragmentDialog作成

スクリーンをOnにした時のイベントを取得した時

private BroadcastReceiver screenStatusReceiver = new BroadcastReceiver() {//スクリーンのON/OFFイベントを検出する
    @Override
    public void onReceive(Context context, Intent intent) {
        // Receive screen off


        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            // OFF
            LoginTimeSave();


        }
        if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            // ON fragmentだとIllegalStateExceptionでる
            LoginReqired();
        }
    }
};

でやるのだが、この時fragmentを生成しようとすると

java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.SCREEN_ON flg=0x50000010 } in com.medicalmonitor.MainActivity$20@451d81d0

Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

が出て強制終了してしまう。

onSaveInstanceState()はonPause()/onStop()の直後に発生するから、onPause()からonResume()の間に操作しなければ解決する。

結果的にcommitAllowingStateLossを使うことで解決できた。

でもこれActivityが再構築された時とかにおかしくなるからあんまり使わないほうがいいらしい。まぁとりあえずこれで様子見。

Fragment prev = getSupportFragmentManager().findFragmentByTag("DigLogin");
if (prev == null) {//これがないと何個もダイアログ作られる

    //Dialog_LoginReqired newFragment = Dialog_LoginReqired.newInstance(null);
    //newFragment.show(getSupportFragmentManager(), "DigLogin");//DigDrugはTAG findFragmentByTagでみつけれる

    Dialog_LoginReqired dialog = new Dialog_LoginReqired();
    //dialog.show(getSupportFragmentManager(), null);
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(dialog, "DigLogin");
    ft.commitAllowingStateLoss();

}

ListViewのクセ

なにかと使い勝手が悪いandroidのListviewだけど、globals関数を想定した使い方のメモ。

まず取り敢えず一番良さそうなのがMainActivityに

 ArrayList<CustomData> localobjects = new ArrayList<CustomData>();//ローカルオブジェクト
 globals.kanjaAdapter = new CustomItemView(this, 0,localobjects);

を定義してadapterを形成。

そしてfragmentに

ListView lv;
lv.setAdapter(globals.kanjaAdapter);

でListviewにアダプターを設定する。

なんかfragmentにadapterを形成してもいいけど、その場合fragmentから離れた場合にあたりまえだけど初期化されるから内容を保持したい場合はやっぱりMainActivityに書くのがよさそう

ちなみによくわからないけどglobals.kanjaobjectsとかobjectにはグローバルは使えなさそう。

なにかとilligalCatchClass的な例外が出る。

でもこれも違うクラスからglobals.kanjaAdapter.addしたりすると動いたり、よくわからない。

とりあえずlocalで定義するのが良さそうだ。

 

追記:globalsで定義して全然問題なかった。

他の処理でglobalsのobjectを操作してたからおかしくなってただけだった。

上の記事はあんまりどころか全く参考にならないかも

DialogでViewpager

DialogでViewpagerを使おうとした時のお話。

どうもAlertDialog.Builderを今まで使ってたのだが、とうとう不具合?が出てしまった。

今回DialogでViewpagerを使うために他のやつのように

// カスタムビューを設定
      LayoutInflater inflater = LayoutInflater.from(this);
      final View layout = inflater.inflate(R.layout.dialog_kusuri, (ViewGroup) findViewById(R.id.layout_root));

のDialogのlayoutからlayout.findViewById(R.id.pager)をViewPagerにしたのだが、何故かViewが見つからなくて落ちてしまう。(java.lang.IllegalStateException: Fragment does not have a view)

頑張ってpagerを見つけようとしたけどダメだった。

これは何でだろーってしらべた結果どうもAlertDialog.Builderを使ってるのが悪かったらしい

これからはDialogFragmentを使えとのこと。

ためしにDialogFragmentを使うとすんなり動いた。

PageDialog.java

package com.medicalmonitor;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;

public class PageDialog extends DialogFragment {
    private final static String KEY_URL = "diaog_key_urls";

    public static PageDialog newInstance(String[] urls) {
        PageDialog fragment = new PageDialog();
        Bundle args = new Bundle();
        args.putStringArray(KEY_URL, urls);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.dialog_kusuri, container, false);
        String[] urls = getArguments().getStringArray(KEY_URL);

        final ViewPager viewPager = (ViewPager) view.findViewById(R.id.pager2);
        final Dialog_Fragment_pager adapter = new Dialog_Fragment_pager(getChildFragmentManager());
        viewPager.setAdapter(adapter);

        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        getDialog().setCanceledOnTouchOutside(true);

        return view;
    }
}

Dialog_Fragment_pager.java

package com.medicalmonitor;


import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.ViewGroup;

import java.util.HashMap;
import java.util.Map;


public class Dialog_Fragment_pager
  extends FragmentPagerAdapter {//FragmentStatePagerAdapterはメモリ破棄する、保存するならこっち
   private Map<Integer, String> mFragmentTags;
    private FragmentManager mFragmentManager;
  public Dialog_Fragment_pager(FragmentManager fm) {
    super(fm);
    mFragmentManager = fm;
    mFragmentTags = new HashMap<Integer, String>();
  }
  private static final String ARG_SECTION_NUMBER = "section_number";

  @Override
  public Fragment getItem(int position) {
     return frag_dialog_pager_main.newInstance(position);
  }

  @Override
  public int getCount() {
    return 3;
  }

  @Override
  public CharSequence getPageTitle(int position) {
     
    return "Page " + position;
  }
  @Override
  public Object instantiateItem(ViewGroup container, int position) {
      Object obj = super.instantiateItem(container, position);
      if (obj instanceof Fragment) {
          // record the fragment tag here.
          Fragment f = (Fragment) obj;
          String tag = f.getTag();
          mFragmentTags.put(position, tag);
      }
      return obj;
  }

  public Fragment getFragment(int position) {
      String tag = mFragmentTags.get(position);
      if (tag == null)
          return null;
      return mFragmentManager.findFragmentByTag(tag);
  }

}

frag_dialog_pager_main.java

package com.medicalmonitor;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONObject;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class frag_dialog_pager_main extends Fragment {
   private static final String KEY_URL_STR = "urlStr";
   private static final int KANJANUM = 0;
   private static final int UNFINISHEDNUM = 1;
   private static final int FINISHEDNUM = 2;
   float juuryou=0;
   int mNum;//画面の番号1はunfinished2はfinishedとか
    final List<CustomData> localobjects = new ArrayList<CustomData>();//ローカルオブジェクト
   static frag_dialog_pager_main newInstance(int num) {
      frag_dialog_pager_main f = new frag_dialog_pager_main();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    /**
     * When creating, retrieve this instance's number from its arguments.
     */
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      mNum = getArguments() != null ? getArguments().getInt("num") : 1;
   }

   
    AlertDialog.Builder alertDlg2;
     private AlertDialog m_dlg;
      View rootView;
       ListView listView;
         //グローバル変数
          Globals globals;
         ArrayAdapter adapter;

            Fragment_pick pick = new Fragment_pick();
           // MainActivity activity = (MainActivity) getActivity();  
            MainActivity main = new MainActivity();
   @Override
   public View onCreateView(
      LayoutInflater inflater, 
      ViewGroup container, 
      Bundle savedInstanceState) {
      globals = (Globals) getActivity().getApplication();
      getActivity().getApplicationContext();

      return inflater.inflate(R.layout.frag_picking_finished, container, false);
   }
   @Override
    public void onStop() {
       //ImageCache.deleteAll(getCacheDir());
       super.onStop();
   
    }
   @Override
    public void onStart() {
       super.onStart();
       
   }
}

↑無駄な処理多いなー

ダイアログ使うときは

PageDialog newFragment = PageDialog.newInstance(null);
newFragment.show(getSupportFragmentManager(), TAG);

だけ!

まぁ動いてよかったー

MySQLで重複コードを除外するクエリ

医薬品データベースでYJコードは同じでもJANコードが違うのはほとんどである。

だからYJコードを主レコードというか重複しないレコードで他のデータベースとLEFT JOINさせたい場合にYJコードが重複してると困っちゃう。

いろいろ考えた結果↓の感じで動いた。

SELECT *
FROM `iyaku_code` AS m
WHERE `JANコード` = (
  SELECT MAX(`JANコード`)
  FROM `iyaku_code` AS s
  WHERE m.`個別医薬品コード` = s.`個別医薬品コード`
);

Showing rows 0 – 24 (20379 total, Query took 0.0542 seconds.)

パフォーマンスもまぁまぁ良かったのでこれを使うことにしよう

MySQLの効率化

いま在庫量のデータベースをいじってる。

固定の医薬品コードとは違いこっちはひたすらデータが積み上がっていくデータベースになる。

だからこれは最初の段階でしっかり設計をしないといけない。

とりあえず現在の在庫のSQLクエリを作ってみた。

 

2つのデータベースを結合して、合計をさせてやった。

 Showing rows 0 – 24 (32 total, Query took 0.0697 seconds.)

SELECT `YJコード`,SUM( `合計` ) FROM (
(SELECT `YJコード`,-SUM( `使用数` ) as 合計 FROM `picking_finished` GROUP BY `YJコード`)
UNION
(SELECT `YJコード`,SUM( `入出数` ) as 合計 FROM `inventory_control` GROUP BY `YJコード`)
)as tmp
GROUP BY YJコード

うんまぁまぁ早い。

しかしここからYJコードからデータをとってきて引っ付けてあーだーこーだしなとイケない。

Accessさんならクエリをクエリ出来て簡単だったけどMySQLはなんかスパゲッティコードになりそうで怖い。

まぁ一つづつ作っていこう。

医薬品のコード

医薬品のコードは別名たくさんある。

困ったものである。

例えば→ 個別医薬品コード=YJコード=販売名コード

ちなみにこの度医薬品コードのデータベースの大幅な改善を行った。

薬価や、薬効、ジェネリックの有無とかに対応さした。

しかし基礎のデータベースを作ったのはもう6ヶ月前。すっかりコードの変換処理とかの仕方は忘れてる。かすかな記憶でなんか今回みたいにデータベースが変更になっても動くようなシステムにしようとは思ってた気がするが全然覚えてない。

試しにCSVでUFT8にしてMySQLに新データを突っ込んだ(大量データでmy.iniのmax_allowed_packet を64Mぐらいにしないと動かなかった)

するとそのまま動いてくれた。

やっぱり最初の設計って大事だと思った瞬間だった。

Activityのローカル変数の保存について

自分のActivityをintentでほかのActivityに渡して再び戻った時にそのActivityが生きてる保証は全くない。

今開発のアプリではglobal関数に結構大事なもの(JSONとか)を保存さしてるのでActivityに戻った時に返り値がnullになってることがしばしばある(Acrobatとかメモリを食うものだとほぼ死ぬ)

なので戻った時にどうしても必要なのはonSaveInstanceStateで保存させるのだけどJSONはどうやって保存させようとなったらStackOverFlowにあったからメモ代わりにおいとく

//保存
if(globals.StockJSON!=null) outState.putString("StockJSON", globals.StockJSON.toString());
try {//JSON READ
    String jsonString = mSavedInstanceState.getString("StockJSON");
    globals.StockJSON = new JSONObject(jsonString);
} catch (JSONException e) {
    e.printStackTrace();
}catch (Exception e) {
    e.printStackTrace();
}

まぁいつもながら効率は考えてない。

しかしまぁAndroid Studioも慣れてきたといえまだまだ知らない事ばかりだ。

Gradleとか全然わからない。

郵便の問題

普段会社というか事務所というか登記されている場所にいないため郵便物が届いたときに困ってしまう。

考えた結果スマートフォンを使ってピンポンの音声に反応して来客モニターの写真を撮ってメールを送信するようなシステムを構築することにした。

auroraってアプリがソースを公開してたからそれをちょこちょこいじって2,3時間ぐらいで完成した。ちゃんと動作することを祈ろう。

これならヤマトさんが来た事が分かるから次の日に事務所に行けば郵送物を受け取れる。

これで1週間おきに事務所に行く手間が省けそうだ。